commit c45b458f6683d58b76d75fe9a113bf58d7ea28bf Author: Joshua Goins Date: Wed Jan 3 16:05:02 2024 -0500 Add old files diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a903283 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +* text=auto + +*.c text +*.cc text +*.cpp text +*.h text +*.hlsl text +*.htm text +*.md text +*.xml text +*.txt text + +3rdparty/* linguist-vendored \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a17d905 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +CMakeCache.txt +CMakeFiles +CMakeScripts +Makefile +cmake_install.cmake +install_manifest.txt +CTestTestfile.cmake +.directory + +build/ +bin/ + +#clion +.idea +cmake-build-debug/ +cmake-build-release/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f6437a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "vendor/glfw"] + path = 3rdparty/glfw + url = git://github.com/glfw/glfw +[submodule "vendor/stb"] + path = 3rdparty/stb + url = git://github.com/nothings/stb.git +[submodule "vendor/json"] + path = 3rdparty/json + url = git://github.com/nlohmann/json.git +[submodule "vendor/glslang"] + path = 3rdparty/glslang + url = git://github.com/KhronosGroup/glslang.git +[submodule "vendor/assimp"] + path = 3rdparty/assimp + url = git://github.com/assimp/assimp.git +[submodule "vendor/glm"] + path = 3rdparty/glm + url = git://github.com/g-truc/glm.git +[submodule "vendor/bullet3"] + path = 3rdparty/bullet3 + url = git://github.com/bulletphysics/bullet3.git diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt new file mode 100644 index 0000000..718f514 --- /dev/null +++ b/3rdparty/CMakeLists.txt @@ -0,0 +1,96 @@ +#this disables all compile warnings for the third party libraries to make it cleaner when building +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") + +add_subdirectory(imgui) +add_subdirectory(angelscript) + +set(CMAKE_DEBUG_POSTFIX "") #stop libraries from vendor messing with our library names + +option(USE_GRAPHICAL_BENCHMARK "" OFF) +option(BUILD_SHARED_LIBS "" OFF) +option(BUILD_CPU_DEMOS "" OFF) +option(BUILD_BULLET3 "" OFF) +option(BUILD_BULLET2_DEMOS "" OFF) +option(BUILD_EXTRAS "" OFF) +option(INSTALL_LIBS "" OFF) +option(BUILD_UNIT_TESTS "" OFF) +option(USE_MSVC_RUNTIME_LIBRARY_DLL "" ON) + +add_subdirectory(bullet3) + +#don't build glfw for emscripten builds +if(NOT EMSCRIPTEN) + option(BUILD_SHARED_LIBS "" OFF) + option(GLFW_BUILD_EXAMPLES "" OFF) + option(GLFW_BUILD_TESTS "" OFF) + option(GLFW_BUILD_DOCS "" OFF) + option(GLFW_INSTALL "" OFF) + + add_subdirectory(glfw) +endif() + +if(ENABLE_VULKAN) + option(ENABLE_GLSLANG_BINARIES "" OFF) + option(ENABLE_HLSL "" OFF) + + add_subdirectory(glslang) +endif() + +add_subdirectory(physfs) + +if(BUILD_EDITOR) + option(BUILD_SHARED_LIBS "" OFF) + option(ASSIMP_OPT_BUILD_PACKAGES "" OFF) + option(ASSIMP_NO_EXPORT "" ON) + option(ASSIMP_BUILD_ASSIMP_TOOLS "" OFF) + option(ASSIMP_BUILD_SAMPLES "" OFF) + option(ASSIMP_BUILD_TESTS "" OFF) + option(ASSIMP_COVERALLS "" OFF) + + #importers + option(ASSIMP_BUILD_AMF_IMPORTER "" OFF) + option(ASSIMP_BUILD_3DS_IMPORTER "" OFF) + option(ASSIMP_BUILD_AC_IMPORTER "" OFF) + option(ASSIMP_BUILD_ASE_IMPORTER "" OFF) + option(ASSIMP_BUILD_ASSBIN_IMPORTER "" OFF) + option(ASSIMP_BUILD_ASSXML_IMPORTER "" OFF) + option(ASSIMP_BUILD_B3D_IMPORTER "" OFF) + option(ASSIMP_BUILD_BVH_IMPORTER "" OFF) + option(ASSIMP_BUILD_DXF_IMPORTER "" OFF) + option(ASSIMP_BUILD_CSM_IMPORTER "" OFF) + option(ASSIMP_BUILD_HMP_IMPORTER "" OFF) + option(ASSIMP_BUILD_IRRMESH_IMPORTER "" OFF) + option(ASSIMP_BUILD_IRR_IMPORTER "" OFF) + option(ASSIMP_BUILD_LWO_IMPORTER "" OFF) + option(ASSIMP_BUILD_LWS_IMPORTER "" OFF) + option(ASSIMP_BUILD_MD2_IMPORTER "" OFF) + option(ASSIMP_BUILD_MD3_IMPORTER "" OFF) + option(ASSIMP_BUILD_MD5_IMPORTER "" OFF) + option(ASSIMP_BUILD_MDC_IMPORTER "" OFF) + option(ASSIMP_BUILD_MDL_IMPORTER "" OFF) + option(ASSIMP_BUILD_NFF_IMPORTER "" OFF) + option(ASSIMP_BUILD_NDO_IMPORTER "" OFF) + option(ASSIMP_BUILD_OGRE_IMPORTER "" OFF) + option(ASSIMP_BUILD_OPENGEX_IMPORTER "" OFF) + option(ASSIMP_BUILD_PLY_IMPORTER "" OFF) + option(ASSIMP_BUILD_MS3D_IMPORTER "" OFF) + option(ASSIMP_BUILD_COB_IMPORTER "" OFF) + option(ASSIMP_BUILD_IFC_IMPORTER "" OFF) + option(ASSIMP_BUILD_XGL_IMPORTER "" OFF) + option(ASSIMP_BUILD_Q3D_IMPORTER "" OFF) + option(ASSIMP_BUILD_Q3BSP_IMPORTER "" OFF) + option(ASSIMP_BUILD_RAW_IMPORTER "" OFF) + option(ASSIMP_BUILD_SIB_IMPORTER "" OFF) + option(ASSIMP_BUILD_SMD_IMPORTER "" OFF) + option(ASSIMP_BUILD_STL_IMPORTER "" OFF) + option(ASSIMP_BUILD_TERRAGEN_IMPORTER "" OFF) + option(ASSIMP_BUILD_3D_IMPORTER "" OFF) + option(ASSIMP_BUILD_X_IMPORTER "" OFF) + option(ASSIMP_BUILD_X3D_IMPORTER "" OFF) + option(ASSIMP_BUILD_GLTF_IMPORTER "" OFF) + option(ASSIMP_BUILD_3MF_IMPORTER "" OFF) + + add_subdirectory(assimp) + + add_subdirectory(ToolWindowManager) +endif() \ No newline at end of file diff --git a/3rdparty/ToolWindowManager/CMakeLists.txt b/3rdparty/ToolWindowManager/CMakeLists.txt new file mode 100644 index 0000000..5566cc5 --- /dev/null +++ b/3rdparty/ToolWindowManager/CMakeLists.txt @@ -0,0 +1,21 @@ +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) + +set(SOURCE_FILES + src/ToolWindowManager.cpp + src/ToolWindowManagerArea.cpp + src/ToolWindowManagerWrapper.cpp) + +set(INCLUDE_FILES + include/ToolWindowManager.h + include/ToolWindowManagerArea.h + include/ToolWindowManagerWrapper.h) + +qt5_wrap_cpp(TWM_SRC ${INCLUDE_FILES}) + +add_library(ToolWindowManager ${SOURCE_FILES} ${TWM_SRC}) + +include_directories(include) + +qt5_use_modules(ToolWindowManager Core Gui Widgets) \ No newline at end of file diff --git a/3rdparty/ToolWindowManager/include/ToolWindowManager.h b/3rdparty/ToolWindowManager/include/ToolWindowManager.h new file mode 100644 index 0000000..4ea8807 --- /dev/null +++ b/3rdparty/ToolWindowManager/include/ToolWindowManager.h @@ -0,0 +1,354 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef TOOLWINDOWMANAGER_H +#define TOOLWINDOWMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class ToolWindowManagerArea; +class ToolWindowManagerWrapper; + + +/*! + * \brief The ToolWindowManager class provides docking tool behavior. + * + * The behavior is similar to tool windows mechanism in Visual Studio or Eclipse. + * User can arrange tool windows + * in tabs, dock it to any border, split with vertical and horizontal splitters, + * tabify them together and detach to floating windows. + * + * See https://github.com/Riateche/toolwindowmanager for detailed description. + */ +class ToolWindowManager : public QWidget { + Q_OBJECT + /*! + * \brief The delay between showing the next suggestion of drop location in milliseconds. + * + * When user starts a tool window drag and moves mouse pointer to a position, there can be + * an ambiguity in new position of the tool window. If user holds the left mouse button and + * stops mouse movements, all possible suggestions will be indicated periodically, one at a time. + * + * Default value is 1000 (i.e. 1 second). + * + * Access functions: suggestionSwitchInterval, setSuggestionSwitchInterval. + * + */ + Q_PROPERTY(int suggestionSwitchInterval READ suggestionSwitchInterval WRITE setSuggestionSwitchInterval) + /*! + * \brief Maximal distance in pixels between mouse position and area border that allows + * to display a suggestion. + * + * Default value is 12. + * + * Access functions: borderSensitivity, setBorderSensitivity. + */ + Q_PROPERTY(int borderSensitivity READ borderSensitivity WRITE setBorderSensitivity) + /*! + * \brief Visible width of rubber band line that is used to display drop suggestions. + * + * Default value is the same as QSplitter::handleWidth default value on current platform. + * + * Access functions: rubberBandLineWidth, setRubberBandLineWidth. + * + */ + Q_PROPERTY(int rubberBandLineWidth READ rubberBandLineWidth WRITE setRubberBandLineWidth) + /*! + * \brief Whether or not to allow floating windows to be created. + * + * Default value is to allow it. + * + * Access functions: allowFloatingWindow, setAllowFloatingWindow. + * + */ + Q_PROPERTY(int allowFloatingWindow READ allowFloatingWindow WRITE setAllowFloatingWindow) + +public: + /*! + * \brief Creates a manager with given \a parent. + */ + explicit ToolWindowManager(QWidget *parent = 0); + /*! + * \brief Destroys the widget. Additionally all tool windows and all floating windows + * created by this widget are destroyed. + */ + virtual ~ToolWindowManager(); + + //! Toolwindow properties + enum ToolWindowProperty { + //! Disables all drag/docking ability by the user + DisallowUserDocking = 0x1, + //! Hides the close button on the tab for this tool window + HideCloseButton = 0x2, + //! Disable the user being able to drag this tab in the tab bar, to rearrange + DisableDraggableTab = 0x4, + //! When the tool window is closed, hide it instead of removing it + HideOnClose = 0x8, + }; + + //! Type of AreaReference. + enum AreaReferenceType { + //! The area tool windows has been added to most recently. + LastUsedArea, + //! New area in a detached window. + NewFloatingArea, + //! Area inside the manager widget (only available when there is no tool windows in it). + EmptySpace, + //! Tool window is hidden. + NoArea, + //! Existing area specified in AreaReference argument. + AddTo, + //! New area to the left of the area specified in AreaReference argument. + LeftOf, + //! New area to the right of the area specified in AreaReference argument. + RightOf, + //! New area to the top of the area specified in AreaReference argument. + TopOf, + //! New area to the bottom of the area specified in AreaReference argument. + BottomOf + }; + + /*! + * \brief The AreaReference class represents a place where tool windows should be moved. + */ + class AreaReference { + public: + /*! + * Creates an area reference of the given \a type. If \a type requires specifying + * area, it should be given in \a area argument. Otherwise \a area should have default value (0). + */ + AreaReference(AreaReferenceType type = NoArea, ToolWindowManagerArea* area = 0, float percentage = 0.5f); + //! Returns type of the reference. + AreaReferenceType type() const { return m_type; } + //! Returns area of the reference, or 0 if it was not specified. + ToolWindowManagerArea* area() const; + + private: + AreaReferenceType m_type; + QWidget* m_widget; + float m_percentage; + QWidget* widget() const { return m_widget; } + float percentage() const { return m_percentage; } + AreaReference(AreaReferenceType type, QWidget* widget); + void setWidget(QWidget* widget); + + friend class ToolWindowManager; + + }; + + /*! + * Adds \a toolWindow to the manager and moves it to the position specified by + * \a area. This function is a shortcut for ToolWindowManager::addToolWindows. + */ + void addToolWindow(QWidget* toolWindow, const AreaReference& area); + + /*! + * Sets the set of \a properties on \a toolWindow that is already added to the manager. + */ + void setToolWindowProperties(QWidget* toolWindow, ToolWindowProperty properties); + + /*! + * Returns the set of \a properties on \a toolWindow. + */ + ToolWindowProperty toolWindowProperties(QWidget* toolWindow); + + /*! + * \brief Adds \a toolWindows to the manager and moves it to the position specified by + * \a area. + * The manager takes ownership of the tool windows and will delete them upon destruction. + * + * toolWindow->windowIcon() and toolWindow->windowTitle() will be used as the icon and title + * of the tab that represents the tool window. + * + * If you intend to use ToolWindowManager::saveState + * and ToolWindowManager::restoreState functions, you must set objectName() of each added + * tool window to a non-empty unique string. + */ + void addToolWindows(QList toolWindows, const AreaReference& area); + + /*! + * Returns area that contains \a toolWindow, or 0 if \a toolWindow is hidden. + */ + ToolWindowManagerArea* areaOf(QWidget* toolWindow); + + /*! + * \brief Moves \a toolWindow to the position specified by \a area. + * + * \a toolWindow must be added to the manager prior to calling this function. + */ + void moveToolWindow(QWidget* toolWindow, AreaReference area); + + /*! + * \brief Moves \a toolWindows to the position specified by \a area. + * + * \a toolWindows must be added to the manager prior to calling this function. + */ + void moveToolWindows(QList toolWindows, AreaReference area); + + /*! + * \brief Removes \a toolWindow from the manager. \a toolWindow becomes a hidden + * top level widget. The ownership of \a toolWindow is returned to the caller. + */ + void removeToolWindow(QWidget* toolWindow); + + /*! + * \brief Returns all tool window added to the manager. + */ + const QList& toolWindows() { return m_toolWindows; } + + /*! + * Hides \a toolWindow. + * + * \a toolWindow must be added to the manager prior to calling this function. + */ + void hideToolWindow(QWidget* toolWindow) { moveToolWindow(toolWindow, NoArea); } + + /*! + * \brief saveState + */ + QVariantMap saveState(); + + /*! + * \brief restoreState + */ + void restoreState(const QVariantMap& data); + + typedef std::function CreateCallback; + + void setToolWindowCreateCallback(const CreateCallback &cb) { m_createCallback = cb; } + QWidget *createToolWindow(const QString& objectName); + + bool checkValidSplitter(QWidget *w); + + /*! \cond PRIVATE */ + void setSuggestionSwitchInterval(int msec); + int suggestionSwitchInterval(); + int borderSensitivity() { return m_borderSensitivity; } + void setBorderSensitivity(int pixels); + void setRubberBandLineWidth(int pixels); + int rubberBandLineWidth() { return m_rubberBandLineWidth; } + void setAllowFloatingWindow(bool pixels); + bool allowFloatingWindow() { return m_allowFloatingWindow; } + /*! \endcond */ + + /*! + * Returns the widget that is used to display rectangular drop suggestions. + */ + QRubberBand* rectRubberBand() { return m_rectRubberBand; } + + /*! + * Returns the widget that is used to display line drop suggestions. + */ + QRubberBand* lineRubberBand() { return m_lineRubberBand; } + + +signals: + /*! + * \brief This signal is emitted when \a toolWindow may be hidden or shown. + * \a visible indicates new visibility state of the tool window. + */ + void toolWindowVisibilityChanged(QWidget* toolWindow, bool visible); + +private: + QList m_toolWindows; // all added tool windows + QHash m_toolWindowProperties; // all tool window properties + QList m_areas; // all areas for this manager + QList m_wrappers; // all wrappers for this manager + int m_borderSensitivity; + int m_rubberBandLineWidth; + // list of tool windows that are currently dragged, or empty list if there is no current drag + QList m_draggedToolWindows; + QLabel* m_dragIndicator; // label used to display dragged content + + QRubberBand* m_rectRubberBand; // placeholder objects used for displaying drop suggestions + QRubberBand* m_lineRubberBand; + bool m_allowFloatingWindow; // Allow floating windows from this docking area + QList m_suggestions; //full list of suggestions for current cursor position + int m_dropCurrentSuggestionIndex; // index of currently displayed drop suggestion + // (e.g. always 0 if there is only one possible drop location) + QTimer m_dropSuggestionSwitchTimer; // used for switching drop suggestions + + CreateCallback m_createCallback; + + // last widget used for adding tool windows, or 0 if there isn't one + // (warning: may contain pointer to deleted object) + ToolWindowManagerArea* m_lastUsedArea; + void handleNoSuggestions(); + //remove tool window from its area (if any) and set parent to 0 + void releaseToolWindow(QWidget* toolWindow); + void simplifyLayout(); //remove constructions that became useless + void startDrag(const QList& toolWindows); + + QVariantMap saveSplitterState(QSplitter* splitter); + QSplitter* restoreSplitterState(const QVariantMap& data); + void findSuggestions(ToolWindowManagerWrapper *wrapper); + QRect sideSensitiveArea(QWidget* widget, AreaReferenceType side); + QRect sidePlaceHolderRect(QWidget* widget, AreaReferenceType side); + + void updateDragPosition(); + void finishDrag(); + bool dragInProgress() { return !m_draggedToolWindows.isEmpty(); } + + friend class ToolWindowManagerArea; + friend class ToolWindowManagerWrapper; + +protected: + /*! + * \brief Creates new splitter and sets its default properties. You may reimplement + * this function to change properties of all splitters used by this class. + */ + virtual QSplitter* createSplitter(); + /*! + * \brief Creates new area and sets its default properties. You may reimplement + * this function to change properties of all tab widgets used by this class. + */ + virtual ToolWindowManagerArea *createArea(); + /*! + * \brief Generates a pixmap that is used to represent the data in a drag and drop operation + * near the mouse cursor. + * You may reimplement this function to use different pixmaps. + */ + virtual QPixmap generateDragPixmap(const QList &toolWindows); + +private slots: + void showNextDropSuggestion(); + void tabCloseRequested(int index); + void windowTitleChanged(const QString &title); + +}; + +inline ToolWindowManager::ToolWindowProperty operator|(ToolWindowManager::ToolWindowProperty a, ToolWindowManager::ToolWindowProperty b) +{ return ToolWindowManager::ToolWindowProperty(int(a) | int(b)); } + +#endif // TOOLWINDOWMANAGER_H diff --git a/3rdparty/ToolWindowManager/include/ToolWindowManagerArea.h b/3rdparty/ToolWindowManager/include/ToolWindowManagerArea.h new file mode 100644 index 0000000..ca474f2 --- /dev/null +++ b/3rdparty/ToolWindowManager/include/ToolWindowManagerArea.h @@ -0,0 +1,110 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef TOOLWINDOWMANAGERAREA_H +#define TOOLWINDOWMANAGERAREA_H + +#include +#include + +class ToolWindowManager; + +/*! + * \brief The ToolWindowManagerArea class is a tab widget used to store tool windows. + * It implements dragging of its tab or the whole tab widget. + */ +class ToolWindowManagerArea : public QTabWidget { + Q_OBJECT +public: + //! Creates new area. + explicit ToolWindowManagerArea(ToolWindowManager* manager, QWidget *parent = 0); + //! Destroys the area. + virtual ~ToolWindowManagerArea(); + + /*! + * Add \a toolWindow to this area. + */ + void addToolWindow(QWidget* toolWindow); + + /*! + * Add \a toolWindows to this area. + */ + void addToolWindows(const QList& toolWindows); + + void enableUserDrop() { m_userCanDrop = true; } + void disableUserDrop() { m_userCanDrop = false; } + + bool allowUserDrop() { return m_userCanDrop; } + + /*! + * Returns a list of all tool windows in this area. + */ + QList toolWindows(); + + ToolWindowManager* manager() { return m_manager; } + + /*! + * Updates the \a toolWindow to its current properties and title. + */ + void updateToolWindow(QWidget* toolWindow); + +protected: + //! Reimplemented from QTabWidget::mousePressEvent. + virtual void mousePressEvent(QMouseEvent *); + //! Reimplemented from QTabWidget::mouseReleaseEvent. + virtual void mouseReleaseEvent(QMouseEvent *); + //! Reimplemented from QTabWidget::mouseMoveEvent. + virtual void mouseMoveEvent(QMouseEvent *); + //! Reimplemented from QTabWidget::eventFilter. + virtual bool eventFilter(QObject *object, QEvent *event); + +private: + ToolWindowManager* m_manager; + bool m_dragCanStart; // indicates that user has started mouse movement on QTabWidget + // that can be considered as dragging it if the cursor will leave + // its area + + bool m_tabDragCanStart; // indicates that user has started mouse movement on QTabWidget + // that can be considered as dragging current tab + // if the cursor will leave the tab bar area + + bool m_userCanDrop; // indictes the user is allowed to drop things on this area + + bool m_inTabMoved; // if we're in the tabMoved() function (so if we call tabMove to cancel + // the movement, we shouldn't re-check the tabMoved behaviour) + + QVariantMap saveState(); // dump contents to variable + void restoreState(const QVariantMap& data); //restore contents from given variable + + //check if mouse left tab widget area so that dragging should start + void check_mouse_move(); + + friend class ToolWindowManager; + friend class ToolWindowManagerWrapper; + +private slots: + void tabMoved(int from, int to); +}; + +#endif // TOOLWINDOWMANAGERAREA_H diff --git a/3rdparty/ToolWindowManager/include/ToolWindowManagerWrapper.h b/3rdparty/ToolWindowManager/include/ToolWindowManagerWrapper.h new file mode 100644 index 0000000..1f9cc0c --- /dev/null +++ b/3rdparty/ToolWindowManager/include/ToolWindowManagerWrapper.h @@ -0,0 +1,66 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef TOOLWINDOWMANAGERWRAPPER_H +#define TOOLWINDOWMANAGERWRAPPER_H + +#include +#include + +class ToolWindowManager; + +/*! + * \brief The ToolWindowManagerWrapper class is used by ToolWindowManager to wrap its content. + * One wrapper is a direct child of the manager and contains tool windows that are inside its window. + * All other wrappers are top level floating windows that contain detached tool windows. + * + */ +class ToolWindowManagerWrapper : public QWidget { + Q_OBJECT +public: + //! Creates new wrapper. + explicit ToolWindowManagerWrapper(ToolWindowManager* manager); + //! Removes the wrapper. + virtual ~ToolWindowManagerWrapper(); + + ToolWindowManager* manager() { return m_manager; } + +protected: + //! Reimplemented to register hiding of contained tool windows when user closes the floating window. + virtual void closeEvent(QCloseEvent *); + +private: + ToolWindowManager* m_manager; + + //dump content's layout to variable + QVariantMap saveState(); + + //construct layout based on given dump + void restoreState(const QVariantMap& data); + + friend class ToolWindowManager; + +}; + +#endif // TOOLWINDOWMANAGERWRAPPER_H diff --git a/3rdparty/ToolWindowManager/src/ToolWindowManager.cpp b/3rdparty/ToolWindowManager/src/ToolWindowManager.cpp new file mode 100644 index 0000000..0416f52 --- /dev/null +++ b/3rdparty/ToolWindowManager/src/ToolWindowManager.cpp @@ -0,0 +1,854 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include "ToolWindowManager.h" +#include "ToolWindowManagerArea.h" +#include "ToolWindowManagerWrapper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +T findClosestParent(QWidget* widget) { + while(widget) { + if (qobject_cast(widget)) { + return static_cast(widget); + } + widget = widget->parentWidget(); + } + return 0; +} + +ToolWindowManager::ToolWindowManager(QWidget *parent) : + QWidget(parent) +{ + m_borderSensitivity = 12; + QSplitter* testSplitter = new QSplitter(); + m_rubberBandLineWidth = testSplitter->handleWidth(); + delete testSplitter; + m_dragIndicator = new QLabel(0, Qt::ToolTip ); + m_dragIndicator->setAttribute(Qt::WA_ShowWithoutActivating); + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + ToolWindowManagerWrapper* wrapper = new ToolWindowManagerWrapper(this); + wrapper->setWindowFlags(wrapper->windowFlags() & ~Qt::Tool); + mainLayout->addWidget(wrapper); + connect(&m_dropSuggestionSwitchTimer, SIGNAL(timeout()), + this, SLOT(showNextDropSuggestion())); + m_dropSuggestionSwitchTimer.setInterval(1000); + m_dropCurrentSuggestionIndex = 0; + m_allowFloatingWindow = true; + m_createCallback = NULL; + + m_rectRubberBand = new QRubberBand(QRubberBand::Rectangle, this); + m_lineRubberBand = new QRubberBand(QRubberBand::Line, this); +} + +ToolWindowManager::~ToolWindowManager() { + while(!m_areas.isEmpty()) { + delete m_areas.first(); + } + while(!m_wrappers.isEmpty()) { + delete m_wrappers.first(); + } +} + +void ToolWindowManager::setToolWindowProperties(QWidget* toolWindow, ToolWindowManager::ToolWindowProperty properties) { + m_toolWindowProperties[toolWindow] = properties; + ToolWindowManagerArea *area = areaOf(toolWindow); + if(area) + area->updateToolWindow(toolWindow); +} + +ToolWindowManager::ToolWindowProperty ToolWindowManager::toolWindowProperties(QWidget* toolWindow) { + return m_toolWindowProperties[toolWindow]; +} + +void ToolWindowManager::addToolWindow(QWidget *toolWindow, const AreaReference &area) { + addToolWindows(QList() << toolWindow, area); +} + +void ToolWindowManager::addToolWindows(QList toolWindows, const ToolWindowManager::AreaReference &area) { + foreach(QWidget* toolWindow, toolWindows) { + if (!toolWindow) { + qWarning("cannot add null widget"); + continue; + } + if (m_toolWindows.contains(toolWindow)) { + qWarning("this tool window has already been added"); + continue; + } + toolWindow->hide(); + toolWindow->setParent(0); + m_toolWindows << toolWindow; + m_toolWindowProperties[toolWindow] = ToolWindowProperty(0); + QObject::connect(toolWindow, &QWidget::windowTitleChanged, this, &ToolWindowManager::windowTitleChanged); + } + moveToolWindows(toolWindows, area); +} + +ToolWindowManagerArea *ToolWindowManager::areaOf(QWidget *toolWindow) { + return findClosestParent(toolWindow); +} + +void ToolWindowManager::moveToolWindow(QWidget *toolWindow, AreaReference area) { + moveToolWindows(QList() << toolWindow, area); +} + +void ToolWindowManager::moveToolWindows(QList toolWindows, + ToolWindowManager::AreaReference area) { + foreach(QWidget* toolWindow, toolWindows) { + if (!m_toolWindows.contains(toolWindow)) { + qWarning("unknown tool window"); + return; + } + if (toolWindow->parentWidget() != 0) { + releaseToolWindow(toolWindow); + } + } + if (area.type() == LastUsedArea && !m_lastUsedArea) { + ToolWindowManagerArea* foundArea = findChild(); + if (foundArea) { + area = AreaReference(AddTo, foundArea); + } else { + area = EmptySpace; + } + } + + if (area.type() == NoArea) { + //do nothing + } else if (area.type() == NewFloatingArea) { + ToolWindowManagerArea* area = createArea(); + area->addToolWindows(toolWindows); + ToolWindowManagerWrapper* wrapper = new ToolWindowManagerWrapper(this); + wrapper->layout()->addWidget(area); + wrapper->move(QCursor::pos()); + wrapper->show(); + } else if (area.type() == AddTo) { + area.area()->addToolWindows(toolWindows); + } else if (area.type() == LeftOf || area.type() == RightOf || + area.type() == TopOf || area.type() == BottomOf) { + QSplitter* parentSplitter = qobject_cast(area.widget()->parentWidget()); + ToolWindowManagerWrapper* wrapper = qobject_cast(area.widget()->parentWidget()); + if (!parentSplitter && !wrapper) { + qWarning("unknown parent type"); + return; + } + bool useParentSplitter = false; + int indexInParentSplitter = 0; + QList parentSplitterGeometries; + QList parentSplitterSizes; + if (parentSplitter) { + indexInParentSplitter = parentSplitter->indexOf(area.widget()); + parentSplitterSizes = parentSplitter->sizes(); + for(int i = 0; i < parentSplitter->count(); i++) { + parentSplitterGeometries.push_back(parentSplitter->widget(i)->geometry()); + } + if (parentSplitter->orientation() == Qt::Vertical) { + useParentSplitter = area.type() == TopOf || area.type() == BottomOf; + } else { + useParentSplitter = area.type() == LeftOf || area.type() == RightOf; + } + } + if (useParentSplitter) { + if (area.type() == BottomOf || area.type() == RightOf) { + indexInParentSplitter++; + } + ToolWindowManagerArea* newArea = createArea(); + newArea->addToolWindows(toolWindows); + parentSplitter->insertWidget(indexInParentSplitter, newArea); + + for(int i = 0; i < qMin(parentSplitter->count(), parentSplitterGeometries.count()); i++) { + parentSplitter->widget(i)->setGeometry(parentSplitterGeometries[i]); + } + } else { + area.widget()->hide(); + area.widget()->setParent(0); + QSplitter* splitter = createSplitter(); + if (area.type() == TopOf || area.type() == BottomOf) { + splitter->setOrientation(Qt::Vertical); + } else { + splitter->setOrientation(Qt::Horizontal); + } + + ToolWindowManagerArea* newArea = createArea(); + + // inherit the size policy from the widget we are wrapping + splitter->setSizePolicy(area.widget()->sizePolicy()); + + // store old geometries so we can restore them + QRect areaGeometry = area.widget()->geometry(); + QRect newGeometry = newArea->geometry(); + + splitter->addWidget(area.widget()); + area.widget()->show(); + + int indexInSplitter = 0; + + if (area.type() == TopOf || area.type() == LeftOf) { + splitter->insertWidget(0, newArea); + indexInSplitter = 0; + } else { + splitter->addWidget(newArea); + indexInSplitter = 1; + } + + // Convert area percentage desired to a stretch factor. + const int totalStretch = 100; + int pct = int(totalStretch*area.percentage()); + splitter->setStretchFactor(indexInSplitter, pct); + splitter->setStretchFactor(1-indexInSplitter, totalStretch-pct); + + if (parentSplitter) { + parentSplitter->insertWidget(indexInParentSplitter, splitter); + + for (int i = 0; i < qMin(parentSplitter->count(), parentSplitterGeometries.count()); i++) { + parentSplitter->widget(i)->setGeometry(parentSplitterGeometries[i]); + } + + if (parentSplitterSizes.count() > 0 && parentSplitterSizes[0] != 0) { + parentSplitter->setSizes(parentSplitterSizes); + } + + } else { + wrapper->layout()->addWidget(splitter); + } + + newArea->addToolWindows(toolWindows); + + area.widget()->setGeometry(areaGeometry); + newArea->setGeometry(newGeometry); + } + } else if (area.type() == EmptySpace) { + ToolWindowManagerArea* newArea = createArea(); + findChild()->layout()->addWidget(newArea); + newArea->addToolWindows(toolWindows); + } else if (area.type() == LastUsedArea) { + m_lastUsedArea->addToolWindows(toolWindows); + } else { + qWarning("invalid type"); + } + simplifyLayout(); + foreach(QWidget* toolWindow, toolWindows) { + emit toolWindowVisibilityChanged(toolWindow, toolWindow->parent() != 0); + } +} + +void ToolWindowManager::removeToolWindow(QWidget *toolWindow) { + if (!m_toolWindows.contains(toolWindow)) { + qWarning("unknown tool window"); + return; + } + moveToolWindow(toolWindow, NoArea); + m_toolWindows.removeOne(toolWindow); + m_toolWindowProperties.remove(toolWindow); + delete toolWindow; + toolWindow = nullptr; +} + +QWidget* ToolWindowManager::createToolWindow(const QString& objectName) +{ + if (m_createCallback) { + QWidget *toolWindow = m_createCallback(objectName); + if(toolWindow) { + m_toolWindows << toolWindow; + m_toolWindowProperties[toolWindow] = ToolWindowProperty(0); + QObject::connect(toolWindow, &QWidget::windowTitleChanged, this, &ToolWindowManager::windowTitleChanged); + return toolWindow; + } + } + + return NULL; +} + +void ToolWindowManager::setSuggestionSwitchInterval(int msec) { + m_dropSuggestionSwitchTimer.setInterval(msec); +} + +int ToolWindowManager::suggestionSwitchInterval() { + return m_dropSuggestionSwitchTimer.interval(); +} + +void ToolWindowManager::setBorderSensitivity(int pixels) { + m_borderSensitivity = pixels; +} + +void ToolWindowManager::setRubberBandLineWidth(int pixels) { + m_rubberBandLineWidth = pixels; +} + +void ToolWindowManager::setAllowFloatingWindow(bool allow) { + m_allowFloatingWindow = allow; +} + +QVariantMap ToolWindowManager::saveState() { + QVariantMap result; + result["toolWindowManagerStateFormat"] = 1; + ToolWindowManagerWrapper* mainWrapper = findChild(); + if (!mainWrapper) { + qWarning("can't find main wrapper"); + return QVariantMap(); + } + result["mainWrapper"] = mainWrapper->saveState(); + QVariantList floatingWindowsData; + foreach(ToolWindowManagerWrapper* wrapper, m_wrappers) { + if (!wrapper->isWindow()) { continue; } + floatingWindowsData << wrapper->saveState(); + } + result["floatingWindows"] = floatingWindowsData; + return result; +} + +void ToolWindowManager::restoreState(const QVariantMap &dataMap) { + if (dataMap.isEmpty()) { return; } + if (dataMap["toolWindowManagerStateFormat"].toInt() != 1) { + qWarning("state format is not recognized"); + return; + } + moveToolWindows(m_toolWindows, NoArea); + ToolWindowManagerWrapper* mainWrapper = findChild(); + if (!mainWrapper) { + qWarning("can't find main wrapper"); + return; + } + mainWrapper->restoreState(dataMap["mainWrapper"].toMap()); + foreach(QVariant windowData, dataMap["floatingWindows"].toList()) { + ToolWindowManagerWrapper* wrapper = new ToolWindowManagerWrapper(this); + wrapper->restoreState(windowData.toMap()); + wrapper->show(); + if(wrapper->windowState() && Qt::WindowMaximized) + { + wrapper->setWindowState(0); + wrapper->setWindowState(Qt::WindowMaximized); + } + } + simplifyLayout(); + foreach(QWidget* toolWindow, m_toolWindows) { + emit toolWindowVisibilityChanged(toolWindow, toolWindow->parentWidget() != 0); + } +} + +ToolWindowManagerArea *ToolWindowManager::createArea() { + ToolWindowManagerArea* area = new ToolWindowManagerArea(this, 0); + connect(area, SIGNAL(tabCloseRequested(int)), + this, SLOT(tabCloseRequested(int))); + return area; +} + + +void ToolWindowManager::handleNoSuggestions() { + m_rectRubberBand->hide(); + m_lineRubberBand->hide(); + m_lineRubberBand->setParent(this); + m_rectRubberBand->setParent(this); + m_suggestions.clear(); + m_dropCurrentSuggestionIndex = 0; + if (m_dropSuggestionSwitchTimer.isActive()) { + m_dropSuggestionSwitchTimer.stop(); + } +} + +void ToolWindowManager::releaseToolWindow(QWidget *toolWindow) { + ToolWindowManagerArea* previousTabWidget = findClosestParent(toolWindow); + if (!previousTabWidget) { + qWarning("cannot find tab widget for tool window"); + return; + } + previousTabWidget->removeTab(previousTabWidget->indexOf(toolWindow)); + toolWindow->hide(); + toolWindow->setParent(0); + +} + +void ToolWindowManager::simplifyLayout() { + foreach(ToolWindowManagerArea* area, m_areas) { + if (area->parentWidget() == 0) { + if (area->count() == 0) { + if (area == m_lastUsedArea) { m_lastUsedArea = 0; } + //QTimer::singleShot(1000, area, SLOT(deleteLater())); + area->deleteLater(); + } + continue; + } + QSplitter* splitter = qobject_cast(area->parentWidget()); + QSplitter* validSplitter = 0; // least top level splitter that should remain + QSplitter* invalidSplitter = 0; //most top level splitter that should be deleted + while(splitter) { + if (splitter->count() > 1) { + validSplitter = splitter; + break; + } else { + invalidSplitter = splitter; + splitter = qobject_cast(splitter->parentWidget()); + } + } + if (!validSplitter) { + ToolWindowManagerWrapper* wrapper = findClosestParent(area); + if (!wrapper) { + qWarning("can't find wrapper"); + return; + } + if (area->count() == 0 && wrapper->isWindow()) { + wrapper->hide(); + // can't deleteLater immediately (strange MacOS bug) + //QTimer::singleShot(1000, wrapper, SLOT(deleteLater())); + wrapper->deleteLater(); + } else if (area->parent() != wrapper) { + wrapper->layout()->addWidget(area); + } + } else { + if (area->count() > 0) { + if (validSplitter && area->parent() != validSplitter) { + int index = validSplitter->indexOf(invalidSplitter); + validSplitter->insertWidget(index, area); + } + } + } + if (invalidSplitter) { + invalidSplitter->hide(); + invalidSplitter->setParent(0); + //QTimer::singleShot(1000, invalidSplitter, SLOT(deleteLater())); + invalidSplitter->deleteLater(); + } + if (area->count() == 0) { + area->hide(); + area->setParent(0); + if (area == m_lastUsedArea) { m_lastUsedArea = 0; } + //QTimer::singleShot(1000, area, SLOT(deleteLater())); + area->deleteLater(); + } + } +} + +void ToolWindowManager::startDrag(const QList &toolWindows) { + if (dragInProgress()) { + qWarning("ToolWindowManager::execDrag: drag is already in progress"); + return; + } + foreach(QWidget* toolWindow, toolWindows) { + if(toolWindowProperties(toolWindow) & DisallowUserDocking) { return; } + } + if (toolWindows.isEmpty()) { return; } + m_draggedToolWindows = toolWindows; + m_dragIndicator->setPixmap(generateDragPixmap(toolWindows)); + updateDragPosition(); + m_dragIndicator->show(); +} + +QVariantMap ToolWindowManager::saveSplitterState(QSplitter *splitter) { + QVariantMap result; + result["state"] = splitter->saveState().toBase64(); + result["type"] = "splitter"; + QVariantList items; + for(int i = 0; i < splitter->count(); i++) { + QWidget* item = splitter->widget(i); + QVariantMap itemValue; + ToolWindowManagerArea* area = qobject_cast(item); + if (area) { + itemValue = area->saveState(); + } else { + QSplitter* childSplitter = qobject_cast(item); + if (childSplitter) { + itemValue = saveSplitterState(childSplitter); + } else { + qWarning("unknown splitter item"); + } + } + items << itemValue; + } + result["items"] = items; + return result; +} + +QSplitter *ToolWindowManager::restoreSplitterState(const QVariantMap &data) { + if (data["items"].toList().count() < 2) { + qWarning("invalid splitter encountered"); + } + QSplitter* splitter = createSplitter(); + + foreach(QVariant itemData, data["items"].toList()) { + QVariantMap itemValue = itemData.toMap(); + QString itemType = itemValue["type"].toString(); + if (itemType == "splitter") { + splitter->addWidget(restoreSplitterState(itemValue)); + } else if (itemType == "area") { + ToolWindowManagerArea* area = createArea(); + area->restoreState(itemValue); + splitter->addWidget(area); + } else { + qWarning("unknown item type"); + } + } + splitter->restoreState(QByteArray::fromBase64(data["state"].toByteArray())); + return splitter; +} + +QPixmap ToolWindowManager::generateDragPixmap(const QList &toolWindows) { + QTabBar widget; + widget.setDocumentMode(true); + foreach(QWidget* toolWindow, toolWindows) { + widget.addTab(toolWindow->windowIcon(), toolWindow->windowTitle()); + } +#if QT_VERSION >= 0x050000 // Qt5 + return widget.grab(); +#else //Qt4 + return QPixmap::grabWidget(&widget); +#endif +} + +void ToolWindowManager::showNextDropSuggestion() { + if (m_suggestions.isEmpty()) { + qWarning("showNextDropSuggestion called but no suggestions"); + return; + } + m_dropCurrentSuggestionIndex++; + if (m_dropCurrentSuggestionIndex >= m_suggestions.count()) { + m_dropCurrentSuggestionIndex = 0; + } + const AreaReference& suggestion = m_suggestions[m_dropCurrentSuggestionIndex]; + if (suggestion.type() == AddTo || suggestion.type() == EmptySpace) { + QWidget* widget; + if (suggestion.type() == EmptySpace) { + widget = findChild(); + } else { + widget = suggestion.widget(); + } + QWidget* placeHolderParent; + if (widget->topLevelWidget() == topLevelWidget()) { + placeHolderParent = this; + } else { + placeHolderParent = widget->topLevelWidget(); + } + QRect placeHolderGeometry = widget->rect(); + placeHolderGeometry.moveTopLeft(widget->mapTo(placeHolderParent, + placeHolderGeometry.topLeft())); + m_rectRubberBand->setGeometry(placeHolderGeometry); + m_rectRubberBand->setParent(placeHolderParent); + m_rectRubberBand->show(); + m_lineRubberBand->hide(); + } else if (suggestion.type() == LeftOf || suggestion.type() == RightOf || + suggestion.type() == TopOf || suggestion.type() == BottomOf) { + QWidget* placeHolderParent; + if (suggestion.widget()->topLevelWidget() == topLevelWidget()) { + placeHolderParent = this; + } else { + placeHolderParent = suggestion.widget()->topLevelWidget(); + } + QRect placeHolderGeometry = sidePlaceHolderRect(suggestion.widget(), suggestion.type()); + placeHolderGeometry.moveTopLeft(suggestion.widget()->mapTo(placeHolderParent, + placeHolderGeometry.topLeft())); + + m_lineRubberBand->setGeometry(placeHolderGeometry); + m_lineRubberBand->setParent(placeHolderParent); + m_lineRubberBand->show(); + m_rectRubberBand->hide(); + } else { + qWarning("unsupported suggestion type"); + } +} + +void ToolWindowManager::findSuggestions(ToolWindowManagerWrapper* wrapper) { + m_suggestions.clear(); + m_dropCurrentSuggestionIndex = -1; + QPoint globalPos = QCursor::pos(); + QList candidates; + foreach(QSplitter* splitter, wrapper->findChildren()) { + // make sure this is one of our layout splitters, not a proper widget or a splitter + // from another manager. We walk the parents, expecting either a QSplitter or QTabWidget + // at each time until we reach an area. + + QWidget *w = splitter; + + bool valid = false; + + while(w) + { + QWidget *parent = w->parentWidget(); + + QSplitter *parentSplitter = qobject_cast(parent); + QTabWidget *parentTab = qobject_cast(parent); + + // keep recursing up what looks like our hierarchy + if(parentSplitter || parentTab) + { + w = parent; + continue; + } + + ToolWindowManagerArea* area = qobject_cast(parent); + ToolWindowManagerWrapper* wrapper = qobject_cast(parent); + + // if it's an area or wrapper, check if it's ours + if(area) + valid = area->manager() == this; + else if(wrapper) + valid = wrapper->manager() == this; + + // we're done now, whether we checked for validity, or if we + // found something that's none of the above + break; + } + + if(valid) + candidates << splitter; + } + foreach(ToolWindowManagerArea* area, m_areas) { + if (area->topLevelWidget() == wrapper->topLevelWidget()) { + candidates << area; + } + } + foreach(QWidget* widget, candidates) { + QSplitter* splitter = qobject_cast(widget); + ToolWindowManagerArea* area = qobject_cast(widget); + if (!splitter && !area) { + qWarning("unexpected widget type"); + continue; + } + QSplitter* parentSplitter = qobject_cast(widget->parentWidget()); + bool lastInSplitter = parentSplitter && + parentSplitter->indexOf(widget) == parentSplitter->count() - 1; + + QList allowedSides; + if (!splitter || splitter->orientation() == Qt::Vertical) { + allowedSides << LeftOf; + } + if (!splitter || splitter->orientation() == Qt::Horizontal) { + allowedSides << TopOf; + } + if (!parentSplitter || parentSplitter->orientation() == Qt::Vertical || lastInSplitter) { + if (!splitter || splitter->orientation() == Qt::Vertical) { + allowedSides << RightOf; + } + } + if (!parentSplitter || parentSplitter->orientation() == Qt::Horizontal || lastInSplitter) { + if (!splitter || splitter->orientation() == Qt::Horizontal) { + allowedSides << BottomOf; + } + } + foreach(AreaReferenceType side, allowedSides) { + if (sideSensitiveArea(widget, side).contains(widget->mapFromGlobal(globalPos))) { + m_suggestions << AreaReference(side, widget); + } + } + if (area && area->allowUserDrop() && area->rect().contains(area->mapFromGlobal(globalPos))) { + m_suggestions << AreaReference(AddTo, area); + } + } + if (candidates.isEmpty()) { + m_suggestions << EmptySpace; + } + + if (m_suggestions.isEmpty()) { + handleNoSuggestions(); + } else { + showNextDropSuggestion(); + } +} + +QRect ToolWindowManager::sideSensitiveArea(QWidget *widget, ToolWindowManager::AreaReferenceType side) { + QRect widgetRect = widget->rect(); + if (side == TopOf) { + return QRect(QPoint(widgetRect.left(), widgetRect.top() - m_borderSensitivity), + QSize(widgetRect.width(), m_borderSensitivity * 2)); + } else if (side == LeftOf) { + return QRect(QPoint(widgetRect.left() - m_borderSensitivity, widgetRect.top()), + QSize(m_borderSensitivity * 2, widgetRect.height())); + + } else if (side == BottomOf) { + return QRect(QPoint(widgetRect.left(), widgetRect.top() + widgetRect.height() - m_borderSensitivity), + QSize(widgetRect.width(), m_borderSensitivity * 2)); + } else if (side == RightOf) { + return QRect(QPoint(widgetRect.left() + widgetRect.width() - m_borderSensitivity, widgetRect.top()), + QSize(m_borderSensitivity * 2, widgetRect.height())); + } else { + qWarning("invalid side"); + return QRect(); + } +} + +QRect ToolWindowManager::sidePlaceHolderRect(QWidget *widget, ToolWindowManager::AreaReferenceType side) { + QRect widgetRect = widget->rect(); + QSplitter* parentSplitter = qobject_cast(widget->parentWidget()); + if (parentSplitter && parentSplitter->indexOf(widget) > 0) { + int delta = parentSplitter->handleWidth() / 2 + m_rubberBandLineWidth / 2; + if (side == TopOf && parentSplitter->orientation() == Qt::Vertical) { + return QRect(QPoint(widgetRect.left(), widgetRect.top() - delta), + QSize(widgetRect.width(), m_rubberBandLineWidth)); + } else if (side == LeftOf && parentSplitter->orientation() == Qt::Horizontal) { + return QRect(QPoint(widgetRect.left() - delta, widgetRect.top()), + QSize(m_rubberBandLineWidth, widgetRect.height())); + } + } + if (side == TopOf) { + return QRect(QPoint(widgetRect.left(), widgetRect.top()), + QSize(widgetRect.width(), m_rubberBandLineWidth)); + } else if (side == LeftOf) { + return QRect(QPoint(widgetRect.left(), widgetRect.top()), + QSize(m_rubberBandLineWidth, widgetRect.height())); + } else if (side == BottomOf) { + return QRect(QPoint(widgetRect.left(), widgetRect.top() + widgetRect.height() - m_rubberBandLineWidth), + QSize(widgetRect.width(), m_rubberBandLineWidth)); + } else if (side == RightOf) { + return QRect(QPoint(widgetRect.left() + widgetRect.width() - m_rubberBandLineWidth, widgetRect.top()), + QSize(m_rubberBandLineWidth, widgetRect.height())); + } else { + qWarning("invalid side"); + return QRect(); + } +} + +void ToolWindowManager::updateDragPosition() { + if (!dragInProgress()) { return; } + if (!(qApp->mouseButtons() & Qt::LeftButton)) { + finishDrag(); + return; + } + + QPoint pos = QCursor::pos(); + m_dragIndicator->move(pos + QPoint(1, 1)); + bool foundWrapper = false; + + QWidget* window = qApp->topLevelAt(pos); + foreach(ToolWindowManagerWrapper* wrapper, m_wrappers) { + if (wrapper->window() == window) { + if (wrapper->rect().contains(wrapper->mapFromGlobal(pos))) { + findSuggestions(wrapper); + if (!m_suggestions.isEmpty()) { + //starting or restarting timer + if (m_dropSuggestionSwitchTimer.isActive()) { + m_dropSuggestionSwitchTimer.stop(); + } + m_dropSuggestionSwitchTimer.start(); + foundWrapper = true; + } + } + break; + } + } + if (!foundWrapper) { + handleNoSuggestions(); + } +} + +void ToolWindowManager::finishDrag() { + if (!dragInProgress()) { + qWarning("unexpected finishDrag"); + return; + } + if (m_suggestions.isEmpty()) { + if (m_allowFloatingWindow) + moveToolWindows(m_draggedToolWindows, NewFloatingArea); + } else { + if (m_dropCurrentSuggestionIndex >= m_suggestions.count()) { + qWarning("invalid m_dropCurrentSuggestionIndex"); + return; + } + ToolWindowManager::AreaReference suggestion = m_suggestions[m_dropCurrentSuggestionIndex]; + handleNoSuggestions(); + moveToolWindows(m_draggedToolWindows, suggestion); + } + + + m_dragIndicator->hide(); + m_draggedToolWindows.clear(); +} + +void ToolWindowManager::tabCloseRequested(int index) { + ToolWindowManagerArea* tabWidget = qobject_cast(sender()); + if (!tabWidget) { + qWarning("sender is not a ToolWindowManagerArea"); + return; + } + QWidget* toolWindow = tabWidget->widget(index); + if (!m_toolWindows.contains(toolWindow)) { + qWarning("unknown tab in tab widget"); + return; + } + + if(toolWindowProperties(toolWindow) & ToolWindowManager::HideOnClose) + hideToolWindow(toolWindow); + else + removeToolWindow(toolWindow); +} + +void ToolWindowManager::windowTitleChanged(const QString&) { + QWidget* toolWindow = qobject_cast(sender()); + if(!toolWindow) { + return; + } + ToolWindowManagerArea *area = areaOf(toolWindow); + if(area) { + area->updateToolWindow(toolWindow); + } +} + +QSplitter *ToolWindowManager::createSplitter() { + QSplitter* splitter = new QSplitter(); + splitter->setChildrenCollapsible(false); + return splitter; +} + +ToolWindowManager::AreaReference::AreaReference(ToolWindowManager::AreaReferenceType type, ToolWindowManagerArea *area, float percentage) { + m_type = type; + m_percentage = percentage; + setWidget(area); +} + +void ToolWindowManager::AreaReference::setWidget(QWidget *widget) { + if (m_type == LastUsedArea || m_type == NewFloatingArea || m_type == NoArea || m_type == EmptySpace) { + if (widget != 0) { + qWarning("area parameter ignored for this type"); + } + m_widget = 0; + } else if (m_type == AddTo) { + m_widget = qobject_cast(widget); + if (!m_widget) { + qWarning("only ToolWindowManagerArea can be used with this type"); + } + } else { + if (!qobject_cast(widget) && + !qobject_cast(widget)) { + qWarning("only ToolWindowManagerArea or splitter can be used with this type"); + m_widget = 0; + } else { + m_widget = widget; + } + } +} + +ToolWindowManagerArea *ToolWindowManager::AreaReference::area() const { + return qobject_cast(m_widget); +} + +ToolWindowManager::AreaReference::AreaReference(ToolWindowManager::AreaReferenceType type, QWidget *widget) { + m_type = type; + setWidget(widget); +} diff --git a/3rdparty/ToolWindowManager/src/ToolWindowManagerArea.cpp b/3rdparty/ToolWindowManager/src/ToolWindowManagerArea.cpp new file mode 100644 index 0000000..251d78b --- /dev/null +++ b/3rdparty/ToolWindowManager/src/ToolWindowManagerArea.cpp @@ -0,0 +1,235 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include "ToolWindowManagerArea.h" +#include "ToolWindowManager.h" +#include +#include +#include + +ToolWindowManagerArea::ToolWindowManagerArea(ToolWindowManager *manager, QWidget *parent) : + QTabWidget(parent) +, m_manager(manager) +{ + m_dragCanStart = false; + m_tabDragCanStart = false; + m_inTabMoved = false; + m_userCanDrop = true; + setMovable(true); + setTabsClosable(true); + setDocumentMode(true); + tabBar()->installEventFilter(this); + m_manager->m_areas << this; + + QObject::connect(tabBar(), &QTabBar::tabMoved, this, &ToolWindowManagerArea::tabMoved); +} + +ToolWindowManagerArea::~ToolWindowManagerArea() { + m_manager->m_areas.removeOne(this); +} + +void ToolWindowManagerArea::addToolWindow(QWidget *toolWindow) { + addToolWindows(QList() << toolWindow); +} + +void ToolWindowManagerArea::addToolWindows(const QList &toolWindows) { + int index = 0; + foreach(QWidget* toolWindow, toolWindows) { + index = addTab(toolWindow, toolWindow->windowIcon(), toolWindow->windowTitle()); + if(m_manager->toolWindowProperties(toolWindow) & ToolWindowManager::HideCloseButton) { + tabBar()->tabButton(index, QTabBar::RightSide)->resize(0, 0); + } + } + setCurrentIndex(index); + m_manager->m_lastUsedArea = this; +} + +QList ToolWindowManagerArea::toolWindows() { + QList result; + for(int i = 0; i < count(); i++) { + result << widget(i); + } + return result; +} + +void ToolWindowManagerArea::updateToolWindow(QWidget* toolWindow) { + int index = indexOf(toolWindow); + if(index >= 0) { + if(m_manager->toolWindowProperties(toolWindow) & ToolWindowManager::HideCloseButton) { + tabBar()->tabButton(index, QTabBar::RightSide)->resize(0, 0); + } else { + tabBar()->tabButton(index, QTabBar::RightSide)->resize(16, 16); + } + tabBar()->setTabText(index, toolWindow->windowTitle()); + } +} + +void ToolWindowManagerArea::mousePressEvent(QMouseEvent *) { + if (qApp->mouseButtons() == Qt::LeftButton) { + m_dragCanStart = true; + } +} + +void ToolWindowManagerArea::mouseReleaseEvent(QMouseEvent *) { + m_dragCanStart = false; + m_manager->updateDragPosition(); +} + +void ToolWindowManagerArea::mouseMoveEvent(QMouseEvent *) { + check_mouse_move(); +} + +bool ToolWindowManagerArea::eventFilter(QObject *object, QEvent *event) { + if (object == tabBar()) { + if (event->type() == QEvent::MouseButtonPress && + qApp->mouseButtons() == Qt::LeftButton) { + + int tabIndex = tabBar()->tabAt(static_cast(event)->pos()); + + // can start tab drag only if mouse is at some tab, not at empty tabbar space + if (tabIndex >= 0) { + m_tabDragCanStart = true; + + if (m_manager->toolWindowProperties(widget(tabIndex)) & ToolWindowManager::DisableDraggableTab) { + setMovable(false); + } else { + setMovable(true); + } + } else { + m_dragCanStart = true; + } + + } else if (event->type() == QEvent::MouseButtonRelease) { + m_tabDragCanStart = false; + m_dragCanStart = false; + m_manager->updateDragPosition(); + } else if (event->type() == QEvent::MouseMove) { + m_manager->updateDragPosition(); + if (m_tabDragCanStart) { + if (tabBar()->rect().contains(static_cast(event)->pos())) { + return false; + } + if (qApp->mouseButtons() != Qt::LeftButton) { + return false; + } + QWidget* toolWindow = currentWidget(); + if (!toolWindow || !m_manager->m_toolWindows.contains(toolWindow)) { + return false; + } + m_tabDragCanStart = false; + //stop internal tab drag in QTabBar + QMouseEvent* releaseEvent = new QMouseEvent(QEvent::MouseButtonRelease, + static_cast(event)->pos(), + Qt::LeftButton, Qt::LeftButton, 0); + qApp->sendEvent(tabBar(), releaseEvent); + m_manager->startDrag(QList() << toolWindow); + } else if (m_dragCanStart) { + check_mouse_move(); + } + } + } + return QTabWidget::eventFilter(object, event); +} + +QVariantMap ToolWindowManagerArea::saveState() { + QVariantMap result; + result["type"] = "area"; + result["currentIndex"] = currentIndex(); + QVariantList objects; + objects.reserve(count()); + for(int i = 0; i < count(); i++) { + QWidget *w = widget(i); + QString name = w->objectName(); + if (name.isEmpty()) { + qWarning("cannot save state of tool window without object name"); + } else { + QVariantMap objectData; + objectData["name"] = name; + objectData["data"] = w->property("persistData"); + objects.push_back(objectData); + } + } + result["objects"] = objects; + return result; +} + +void ToolWindowManagerArea::restoreState(const QVariantMap &data) { + foreach(QVariant object, data["objects"].toList()) { + QVariantMap objectData = object.toMap(); + if (objectData.isEmpty()) { continue; } + QString objectName = objectData["name"].toString(); + if (objectName.isEmpty()) { continue; } + QWidget *t = NULL; + foreach(QWidget* toolWindow, m_manager->m_toolWindows) { + if (toolWindow->objectName() == objectName) { + t = toolWindow; + break; + } + } + if (t == NULL) t = m_manager->createToolWindow(objectName); + if (t) { + t->setProperty("persistData", objectData["data"]); + addToolWindow(t); + } else { + qWarning("tool window with name '%s' not found or created", objectName.toLocal8Bit().constData()); + } + } + setCurrentIndex(data["currentIndex"].toInt()); +} + +void ToolWindowManagerArea::check_mouse_move() { + m_manager->updateDragPosition(); + if (qApp->mouseButtons() == Qt::LeftButton && + !rect().contains(mapFromGlobal(QCursor::pos())) && + m_dragCanStart) { + m_dragCanStart = false; + QList toolWindows; + for(int i = 0; i < count(); i++) { + QWidget* toolWindow = widget(i); + if (!m_manager->m_toolWindows.contains(toolWindow)) { + qWarning("tab widget contains unmanaged widget"); + } else { + toolWindows << toolWindow; + } + } + m_manager->startDrag(toolWindows); + } +} + +void ToolWindowManagerArea::tabMoved(int from, int to) { + if(m_inTabMoved) return; + + QWidget *a = widget(from); + QWidget *b = widget(to); + + if(!a || !b) return; + + if(m_manager->toolWindowProperties(a) & ToolWindowManager::DisableDraggableTab || + m_manager->toolWindowProperties(b) & ToolWindowManager::DisableDraggableTab) + { + m_inTabMoved = true; + tabBar()->moveTab(to, from); + m_inTabMoved = false; + } +} diff --git a/3rdparty/ToolWindowManager/src/ToolWindowManagerWrapper.cpp b/3rdparty/ToolWindowManager/src/ToolWindowManagerWrapper.cpp new file mode 100644 index 0000000..3d3ed3e --- /dev/null +++ b/3rdparty/ToolWindowManager/src/ToolWindowManagerWrapper.cpp @@ -0,0 +1,97 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Strakhov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include "ToolWindowManagerWrapper.h" +#include "ToolWindowManager.h" +#include "ToolWindowManagerArea.h" +#include +#include +#include +#include +#include + +ToolWindowManagerWrapper::ToolWindowManagerWrapper(ToolWindowManager *manager) : + QWidget(manager) +, m_manager(manager) +{ + setWindowFlags(windowFlags() | Qt::Tool); + setWindowTitle(" "); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + m_manager->m_wrappers << this; +} + +ToolWindowManagerWrapper::~ToolWindowManagerWrapper() { + m_manager->m_wrappers.removeOne(this); +} + +void ToolWindowManagerWrapper::closeEvent(QCloseEvent *) { + QList toolWindows; + foreach(ToolWindowManagerArea* tabWidget, findChildren()) { + toolWindows << tabWidget->toolWindows(); + } + m_manager->moveToolWindows(toolWindows, ToolWindowManager::NoArea); +} + +QVariantMap ToolWindowManagerWrapper::saveState() { + if (layout()->count() > 1) { + qWarning("too many children for wrapper"); + return QVariantMap(); + } + if (isWindow() && layout()->count() == 0) { + qWarning("empty top level wrapper"); + return QVariantMap(); + } + QVariantMap result; + result["geometry"] = saveGeometry().toBase64(); + QSplitter* splitter = findChild(QString(), Qt::FindDirectChildrenOnly); + if (splitter) { + result["splitter"] = m_manager->saveSplitterState(splitter); + } else { + ToolWindowManagerArea* area = findChild(); + if (area) { + result["area"] = area->saveState(); + } else if (layout()->count() > 0) { + qWarning("unknown child"); + return QVariantMap(); + } + } + return result; +} + +void ToolWindowManagerWrapper::restoreState(const QVariantMap &data) { + restoreGeometry(QByteArray::fromBase64(data["geometry"].toByteArray())); + if (layout()->count() > 0) { + qWarning("wrapper is not empty"); + return; + } + if (data.contains("splitter")) { + layout()->addWidget(m_manager->restoreSplitterState(data["splitter"].toMap())); + } else if (data.contains("area")) { + ToolWindowManagerArea* area = m_manager->createArea(); + area->restoreState(data["area"].toMap()); + layout()->addWidget(area); + } +} diff --git a/3rdparty/angelscript/CMakeLists.txt b/3rdparty/angelscript/CMakeLists.txt new file mode 100644 index 0000000..6f36a82 --- /dev/null +++ b/3rdparty/angelscript/CMakeLists.txt @@ -0,0 +1,128 @@ +project(Angelscript) + +set(INCLUDE_FILES + include/angelscript.h + include/as_array.h + include/as_atomic.h + include/as_builder.h + include/as_bytecode.h + include/as_callfunc.h + include/as_compiler.h + include/as_config.h + include/as_configgroup.h + include/as_context.h + include/as_criticalsection.h + include/as_datatype.h + include/as_debug.h + include/as_gc.h + include/as_generic.h + include/as_map.h + include/as_memory.h + include/as_module.h + include/as_namespace.h + include/as_objecttype.h + include/as_outputbuffer.h + include/as_parser.h + include/as_property.h + include/as_restore.h + include/as_scriptcode.h + include/as_scriptengine.h + include/as_scriptfunction.h + include/as_scriptnode.h + include/as_scriptobject.h + include/as_string.h + include/as_string_util.h + include/as_symboltable.h + include/as_texts.h + include/as_thread.h + include/as_tokendef.h + include/as_tokenizer.h + include/as_typeinfo.h + include/as_variablescope.h + include/scriptstdstring.h + include/scriptarray.h) + +set(SOURCE_FILES + src/as_atomic.cpp + src/as_builder.cpp + src/as_bytecode.cpp + src/as_callfunc.cpp + src/as_callfunc_mips.cpp + src/as_callfunc_x86.cpp + src/as_callfunc_x64_gcc.cpp + src/as_callfunc_x64_msvc.cpp + src/as_callfunc_x64_mingw.cpp + src/as_compiler.cpp + src/as_configgroup.cpp + src/as_context.cpp + src/as_datatype.cpp + src/as_gc.cpp + src/as_generic.cpp + src/as_globalproperty.cpp + src/as_memory.cpp + src/as_module.cpp + src/as_objecttype.cpp + src/as_outputbuffer.cpp + src/as_parser.cpp + src/as_restore.cpp + src/as_scriptcode.cpp + src/as_scriptengine.cpp + src/as_scriptfunction.cpp + src/as_scriptnode.cpp + src/as_scriptobject.cpp + src/as_string.cpp + src/as_string_util.cpp + src/as_thread.cpp + src/as_tokenizer.cpp + src/as_typeinfo.cpp + src/as_variablescope.cpp + src/scriptstdstring.cpp + src/scriptstdstring_utils.cpp + src/scriptarray.cpp) + +add_definitions(-DANGELSCRIPT_EXPORT -D_LIB) + +if(MSVC AND CMAKE_CL_64) + enable_language(ASM_MASM) + if(CMAKE_ASM_MASM_COMPILER_WORKS) + set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} src/as_callfunc_x64_msvc_asm.asm) + else() + message(FATAL ERROR "MSVC x86_64 target requires a working assembler") + endif() +endif() + +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm") + enable_language(ASM) + if(CMAKE_ASM_COMPILER_WORKS) + set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} src/as_callfunc_arm.cpp src/as_callfunc_arm_gcc.S) + set_property(SOURCE src/as_callfunc_arm_gcc.S APPEND PROPERTY COMPILE_FLAGS " -Wa,-mimplicit-it=always") + else() + message(FATAL ERROR "ARM target requires a working assembler") + endif() +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +if(MSVC) + set(CMAKE_DEBUG_POSTFIX "d") + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# Fix x64 issues on Linux +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE) + add_definitions(-fPIC) +endif() + +add_library(Angelscript ${SOURCE_FILES} ${INCLUDE_FILES}) + +target_link_libraries(Angelscript ${CMAKE_THREAD_LIBS_INIT}) + +if(UNIX) + target_link_libraries(Angelscript pthread) +endif() + +include_directories(include) + +if(MSVC) + set_target_properties(${ANGELSCRIPT_LIBRARY_NAME} PROPERTIES COMPILE_FLAGS "/MP") +endif() \ No newline at end of file diff --git a/3rdparty/angelscript/LICENSE b/3rdparty/angelscript/LICENSE new file mode 100644 index 0000000..70aabb4 --- /dev/null +++ b/3rdparty/angelscript/LICENSE @@ -0,0 +1,13 @@ +AngelCode Scripting Library + +Copyright © 2003-2016 Andreas Jönsson + +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: + + 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. + + Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + + This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/3rdparty/angelscript/include/angelscript.h b/3rdparty/angelscript/include/angelscript.h new file mode 100644 index 0000000..f12269a --- /dev/null +++ b/3rdparty/angelscript/include/angelscript.h @@ -0,0 +1,1983 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// angelscript.h +// +// The script engine interface +// + + +#ifndef ANGELSCRIPT_H +#define ANGELSCRIPT_H + +#include +#ifndef _MSC_VER +#include +#endif + +#ifdef AS_USE_NAMESPACE + #define BEGIN_AS_NAMESPACE namespace AngelScript { + #define END_AS_NAMESPACE } + #define AS_NAMESPACE_QUALIFIER AngelScript:: +#else + #define BEGIN_AS_NAMESPACE + #define END_AS_NAMESPACE + #define AS_NAMESPACE_QUALIFIER :: +#endif + +BEGIN_AS_NAMESPACE + +// AngelScript version + +#define ANGELSCRIPT_VERSION 23102 +#define ANGELSCRIPT_VERSION_STRING "2.31.2" + +// Data types + +class asIScriptEngine; +class asIScriptModule; +class asIScriptContext; +class asIScriptGeneric; +class asIScriptObject; +class asITypeInfo; +#ifdef AS_DEPRECATED +// deprecated since 2.31.0 - 2015/11/18 +typedef asITypeInfo asIObjectType; +#endif +class asIScriptFunction; +class asIBinaryStream; +class asIJITCompiler; +class asIThreadManager; +class asILockableSharedBool; + +// Enumerations and constants + +// Return codes +enum asERetCodes +{ + asSUCCESS = 0, + asERROR = -1, + asCONTEXT_ACTIVE = -2, + asCONTEXT_NOT_FINISHED = -3, + asCONTEXT_NOT_PREPARED = -4, + asINVALID_ARG = -5, + asNO_FUNCTION = -6, + asNOT_SUPPORTED = -7, + asINVALID_NAME = -8, + asNAME_TAKEN = -9, + asINVALID_DECLARATION = -10, + asINVALID_OBJECT = -11, + asINVALID_TYPE = -12, + asALREADY_REGISTERED = -13, + asMULTIPLE_FUNCTIONS = -14, + asNO_MODULE = -15, + asNO_GLOBAL_VAR = -16, + asINVALID_CONFIGURATION = -17, + asINVALID_INTERFACE = -18, + asCANT_BIND_ALL_FUNCTIONS = -19, + asLOWER_ARRAY_DIMENSION_NOT_REGISTERED = -20, + asWRONG_CONFIG_GROUP = -21, + asCONFIG_GROUP_IS_IN_USE = -22, + asILLEGAL_BEHAVIOUR_FOR_TYPE = -23, + asWRONG_CALLING_CONV = -24, + asBUILD_IN_PROGRESS = -25, + asINIT_GLOBAL_VARS_FAILED = -26, + asOUT_OF_MEMORY = -27, + asMODULE_IS_IN_USE = -28 +}; + +// Engine properties +enum asEEngineProp +{ + asEP_ALLOW_UNSAFE_REFERENCES = 1, + asEP_OPTIMIZE_BYTECODE = 2, + asEP_COPY_SCRIPT_SECTIONS = 3, + asEP_MAX_STACK_SIZE = 4, + asEP_USE_CHARACTER_LITERALS = 5, + asEP_ALLOW_MULTILINE_STRINGS = 6, + asEP_ALLOW_IMPLICIT_HANDLE_TYPES = 7, + asEP_BUILD_WITHOUT_LINE_CUES = 8, + asEP_INIT_GLOBAL_VARS_AFTER_BUILD = 9, + asEP_REQUIRE_ENUM_SCOPE = 10, + asEP_SCRIPT_SCANNER = 11, + asEP_INCLUDE_JIT_INSTRUCTIONS = 12, + asEP_STRING_ENCODING = 13, + asEP_PROPERTY_ACCESSOR_MODE = 14, + asEP_EXPAND_DEF_ARRAY_TO_TMPL = 15, + asEP_AUTO_GARBAGE_COLLECT = 16, + asEP_DISALLOW_GLOBAL_VARS = 17, + asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT = 18, + asEP_COMPILER_WARNINGS = 19, + asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE = 20, + asEP_ALTER_SYNTAX_NAMED_ARGS = 21, + asEP_DISABLE_INTEGER_DIVISION = 22, + asEP_DISALLOW_EMPTY_LIST_ELEMENTS = 23, + asEP_PRIVATE_PROP_AS_PROTECTED = 24, + asEP_ALLOW_UNICODE_IDENTIFIERS = 25, + asEP_HEREDOC_TRIM_MODE = 26, + + asEP_LAST_PROPERTY +}; + +// Calling conventions +enum asECallConvTypes +{ + asCALL_CDECL = 0, + asCALL_STDCALL = 1, + asCALL_THISCALL_ASGLOBAL = 2, + asCALL_THISCALL = 3, + asCALL_CDECL_OBJLAST = 4, + asCALL_CDECL_OBJFIRST = 5, + asCALL_GENERIC = 6, + asCALL_THISCALL_OBJLAST = 7, + asCALL_THISCALL_OBJFIRST = 8 +}; + +// Object type flags +enum asEObjTypeFlags +{ + asOBJ_REF = (1<<0), + asOBJ_VALUE = (1<<1), + asOBJ_GC = (1<<2), + asOBJ_POD = (1<<3), + asOBJ_NOHANDLE = (1<<4), + asOBJ_SCOPED = (1<<5), + asOBJ_TEMPLATE = (1<<6), + asOBJ_ASHANDLE = (1<<7), + asOBJ_APP_CLASS = (1<<8), + asOBJ_APP_CLASS_CONSTRUCTOR = (1<<9), + asOBJ_APP_CLASS_DESTRUCTOR = (1<<10), + asOBJ_APP_CLASS_ASSIGNMENT = (1<<11), + asOBJ_APP_CLASS_COPY_CONSTRUCTOR = (1<<12), + asOBJ_APP_CLASS_C = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR), + asOBJ_APP_CLASS_CD = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_CA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_CK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CDA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_CDK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CDAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_D = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_DA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_DK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_DAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_A = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_AK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_K = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_PRIMITIVE = (1<<13), + asOBJ_APP_FLOAT = (1<<14), + asOBJ_APP_ARRAY = (1<<15), + asOBJ_APP_CLASS_ALLINTS = (1<<16), + asOBJ_APP_CLASS_ALLFLOATS = (1<<17), + asOBJ_NOCOUNT = (1<<18), + asOBJ_APP_CLASS_ALIGN8 = (1<<19), + asOBJ_IMPLICIT_HANDLE = (1<<20), + asOBJ_MASK_VALID_FLAGS = 0x1FFFFF, + // Internal flags + asOBJ_SCRIPT_OBJECT = (1<<21), + asOBJ_SHARED = (1<<22), + asOBJ_NOINHERIT = (1<<23), + asOBJ_FUNCDEF = (1<<24), + asOBJ_LIST_PATTERN = (1<<25), + asOBJ_ENUM = (1<<26), + asOBJ_TEMPLATE_SUBTYPE = (1<<27), + asOBJ_TYPEDEF = (1<<28), + asOBJ_ABSTRACT = (1<<29), + asOBJ_APP_ALIGN16 = (1<<30) +}; + +// Behaviours +enum asEBehaviours +{ + // Value object memory management + asBEHAVE_CONSTRUCT, + asBEHAVE_LIST_CONSTRUCT, + asBEHAVE_DESTRUCT, + + // Reference object memory management + asBEHAVE_FACTORY, + asBEHAVE_LIST_FACTORY, + asBEHAVE_ADDREF, + asBEHAVE_RELEASE, + asBEHAVE_GET_WEAKREF_FLAG, + + // Object operators +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0, 2014-10-24 + asBEHAVE_VALUE_CAST, + asBEHAVE_IMPLICIT_VALUE_CAST, + // Deprecated since 2.30.0, 2014-12-30 + asBEHAVE_REF_CAST, + asBEHAVE_IMPLICIT_REF_CAST, +#endif + asBEHAVE_TEMPLATE_CALLBACK, + + // Garbage collection behaviours + asBEHAVE_FIRST_GC, + asBEHAVE_GETREFCOUNT = asBEHAVE_FIRST_GC, + asBEHAVE_SETGCFLAG, + asBEHAVE_GETGCFLAG, + asBEHAVE_ENUMREFS, + asBEHAVE_RELEASEREFS, + asBEHAVE_LAST_GC = asBEHAVE_RELEASEREFS, + + asBEHAVE_MAX +}; + +// Context states +enum asEContextState +{ + asEXECUTION_FINISHED = 0, + asEXECUTION_SUSPENDED = 1, + asEXECUTION_ABORTED = 2, + asEXECUTION_EXCEPTION = 3, + asEXECUTION_PREPARED = 4, + asEXECUTION_UNINITIALIZED = 5, + asEXECUTION_ACTIVE = 6, + asEXECUTION_ERROR = 7 +}; + +// Message types +enum asEMsgType +{ + asMSGTYPE_ERROR = 0, + asMSGTYPE_WARNING = 1, + asMSGTYPE_INFORMATION = 2 +}; + +// Garbage collector flags +enum asEGCFlags +{ + asGC_FULL_CYCLE = 1, + asGC_ONE_STEP = 2, + asGC_DESTROY_GARBAGE = 4, + asGC_DETECT_GARBAGE = 8 +}; + +// Token classes +enum asETokenClass +{ + asTC_UNKNOWN = 0, + asTC_KEYWORD = 1, + asTC_VALUE = 2, + asTC_IDENTIFIER = 3, + asTC_COMMENT = 4, + asTC_WHITESPACE = 5 +}; + +// Type id flags +enum asETypeIdFlags +{ + asTYPEID_VOID = 0, + asTYPEID_BOOL = 1, + asTYPEID_INT8 = 2, + asTYPEID_INT16 = 3, + asTYPEID_INT32 = 4, + asTYPEID_INT64 = 5, + asTYPEID_UINT8 = 6, + asTYPEID_UINT16 = 7, + asTYPEID_UINT32 = 8, + asTYPEID_UINT64 = 9, + asTYPEID_FLOAT = 10, + asTYPEID_DOUBLE = 11, + asTYPEID_OBJHANDLE = 0x40000000, + asTYPEID_HANDLETOCONST = 0x20000000, + asTYPEID_MASK_OBJECT = 0x1C000000, + asTYPEID_APPOBJECT = 0x04000000, + asTYPEID_SCRIPTOBJECT = 0x08000000, + asTYPEID_TEMPLATE = 0x10000000, + asTYPEID_MASK_SEQNBR = 0x03FFFFFF +}; + +// Type modifiers +enum asETypeModifiers +{ + asTM_NONE = 0, + asTM_INREF = 1, + asTM_OUTREF = 2, + asTM_INOUTREF = 3, + asTM_CONST = 4 +}; + +// GetModule flags +enum asEGMFlags +{ + asGM_ONLY_IF_EXISTS = 0, + asGM_CREATE_IF_NOT_EXISTS = 1, + asGM_ALWAYS_CREATE = 2 +}; + +// Compile flags +enum asECompileFlags +{ + asCOMP_ADD_TO_MODULE = 1 +}; + +// Function types +enum asEFuncType +{ + asFUNC_DUMMY =-1, + asFUNC_SYSTEM = 0, + asFUNC_SCRIPT = 1, + asFUNC_INTERFACE = 2, + asFUNC_VIRTUAL = 3, + asFUNC_FUNCDEF = 4, + asFUNC_IMPORTED = 5, + asFUNC_DELEGATE = 6 +}; + +// +// asBYTE = 8 bits +// asWORD = 16 bits +// asDWORD = 32 bits +// asQWORD = 64 bits +// asPWORD = size of pointer +// +typedef unsigned char asBYTE; +typedef unsigned short asWORD; +typedef unsigned int asUINT; +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__S3E__) || (defined(_MSC_VER) && defined(__clang__)) + // size_t is not really correct, since it only guaranteed to be large enough to hold the segment size. + // For example, on 16bit systems the size_t may be 16bits only even if pointers are 32bit. But nobody + // is likely to use MSVC6 to compile for 16bit systems anymore, so this should be ok. + typedef size_t asPWORD; +#else + typedef uintptr_t asPWORD; +#endif +#ifdef __LP64__ + typedef unsigned int asDWORD; + typedef unsigned long asQWORD; + typedef long asINT64; +#else + typedef unsigned long asDWORD; + #if !defined(_MSC_VER) && (defined(__GNUC__) || defined(__MWERKS__) || defined(__SUNPRO_CC) || defined(__psp2__)) + typedef uint64_t asQWORD; + typedef int64_t asINT64; + #else + typedef unsigned __int64 asQWORD; + typedef __int64 asINT64; + #endif +#endif + +// Is the target a 64bit system? +#if defined(__LP64__) || defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) + #ifndef AS_64BIT_PTR + #define AS_64BIT_PTR + #endif +#endif + +typedef void (*asFUNCTION_t)(); +typedef void (*asGENFUNC_t)(asIScriptGeneric *); +typedef void *(*asALLOCFUNC_t)(size_t); +typedef void (*asFREEFUNC_t)(void *); +typedef void (*asCLEANENGINEFUNC_t)(asIScriptEngine *); +typedef void (*asCLEANMODULEFUNC_t)(asIScriptModule *); +typedef void (*asCLEANCONTEXTFUNC_t)(asIScriptContext *); +typedef void (*asCLEANFUNCTIONFUNC_t)(asIScriptFunction *); +typedef void (*asCLEANTYPEINFOFUNC_t)(asITypeInfo *); +#ifdef AS_DEPRECATED +// deprecated since 2.31.0 - 2015/11/18 +typedef asCLEANTYPEINFOFUNC_t asCLEANOBJECTTYPEFUNC_t; +#endif +typedef void (*asCLEANSCRIPTOBJECTFUNC_t)(asIScriptObject *); +typedef asIScriptContext *(*asREQUESTCONTEXTFUNC_t)(asIScriptEngine *, void *); +typedef void (*asRETURNCONTEXTFUNC_t)(asIScriptEngine *, asIScriptContext *, void *); + +// Check if the compiler can use C++11 features +#if !defined(_MSC_VER) || _MSC_VER >= 1700 // MSVC 2012 + #if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) // gnuc 4.7 or clang + #if !(defined(__GNUC__) && defined(__cplusplus) && __cplusplus < 201103L) // gnuc and clang require compiler flag -std=c++11 + #if !defined(__SUNPRO_CC) // Oracle Solaris Studio + #define AS_CAN_USE_CPP11 1 + #endif + #endif + #endif +#endif + +// This macro does basically the same thing as offsetof defined in stddef.h, but +// GNUC should not complain about the usage as I'm not using 0 as the base pointer. +#define asOFFSET(s,m) ((size_t)(&reinterpret_cast(100000)->m)-100000) + +#define asFUNCTION(f) asFunctionPtr(f) +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || (defined(__BORLANDC__) && __BORLANDC__ < 0x590) +// MSVC 6 has a bug that prevents it from properly compiling using the correct asFUNCTIONPR with operator > +// so we need to use ordinary C style cast instead of static_cast. The drawback is that the compiler can't +// check that the cast is really valid. +// BCC v5.8 (C++Builder 2006) and earlier have a similar bug which forces us to fall back to a C-style cast. +#define asFUNCTIONPR(f,p,r) asFunctionPtr((void (*)())((r (*)p)(f))) +#else +#define asFUNCTIONPR(f,p,r) asFunctionPtr((void (*)())(static_cast(f))) +#endif + +#ifndef AS_NO_CLASS_METHODS + +class asCUnknownClass; +typedef void (asCUnknownClass::*asMETHOD_t)(); + +struct asSFuncPtr +{ + asSFuncPtr(asBYTE f = 0) + { + for( size_t n = 0; n < sizeof(ptr.dummy); n++ ) + ptr.dummy[n] = 0; + flag = f; + } + + void CopyMethodPtr(const void *mthdPtr, size_t size) + { + for( size_t n = 0; n < size; n++ ) + ptr.dummy[n] = reinterpret_cast(mthdPtr)[n]; + } + + union + { + // The largest known method point is 20 bytes (MSVC 64bit), + // but with 8byte alignment this becomes 24 bytes. So we need + // to be able to store at least that much. + char dummy[25]; + struct {asMETHOD_t mthd; char dummy[25-sizeof(asMETHOD_t)];} m; + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func, 3 = method +}; + +#if defined(__BORLANDC__) +// A bug in BCC (QC #85374) makes it impossible to distinguish const/non-const method overloads +// with static_cast<>. The workaround is to use an _implicit_cast instead. + + #if __BORLANDC__ < 0x590 + // BCC v5.8 (C++Builder 2006) and earlier have an even more annoying bug which causes + // the "pretty" workaround below (with _implicit_cast<>) to fail. For these compilers + // we need to use a traditional C-style cast. + #define AS_METHOD_AMBIGUITY_CAST(t) (t) + #else +template + T _implicit_cast (T val) +{ return val; } + #define AS_METHOD_AMBIGUITY_CAST(t) AS_NAMESPACE_QUALIFIER _implicit_cast + #endif +#else + #define AS_METHOD_AMBIGUITY_CAST(t) static_cast +#endif + +#define asMETHOD(c,m) asSMethodPtr::Convert((void (c::*)())(&c::m)) +#define asMETHODPR(c,m,p,r) asSMethodPtr::Convert(AS_METHOD_AMBIGUITY_CAST(r (c::*)p)(&c::m)) + +#else // Class methods are disabled + +struct asSFuncPtr +{ + asSFuncPtr(asBYTE f) + { + for( int n = 0; n < sizeof(ptr.dummy); n++ ) + ptr.dummy[n] = 0; + flag = f; + } + + union + { + char dummy[25]; // largest known class method pointer + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func +}; + +#endif + +struct asSMessageInfo +{ + const char *section; + int row; + int col; + asEMsgType type; + const char *message; +}; + + +// API functions + +// ANGELSCRIPT_EXPORT is defined when compiling the dll or lib +// ANGELSCRIPT_DLL_LIBRARY_IMPORT is defined when dynamically linking to the +// dll through the link lib automatically generated by MSVC++ +// ANGELSCRIPT_DLL_MANUAL_IMPORT is defined when manually loading the dll +// Don't define anything when linking statically to the lib + +#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) + #if defined(ANGELSCRIPT_EXPORT) + #define AS_API __declspec(dllexport) + #elif defined(ANGELSCRIPT_DLL_LIBRARY_IMPORT) + #define AS_API __declspec(dllimport) + #else // statically linked library + #define AS_API + #endif +#elif defined(__GNUC__) + #if defined(ANGELSCRIPT_EXPORT) + #define AS_API __attribute__((visibility ("default"))) + #else + #define AS_API + #endif +#else + #define AS_API +#endif + +#ifndef ANGELSCRIPT_DLL_MANUAL_IMPORT +extern "C" +{ + // Engine + AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version = ANGELSCRIPT_VERSION); + AS_API const char *asGetLibraryVersion(); + AS_API const char *asGetLibraryOptions(); + + // Context + AS_API asIScriptContext *asGetActiveContext(); + + // Thread support + AS_API int asPrepareMultithread(asIThreadManager *externalMgr = 0); + AS_API void asUnprepareMultithread(); + AS_API asIThreadManager *asGetThreadManager(); + AS_API void asAcquireExclusiveLock(); + AS_API void asReleaseExclusiveLock(); + AS_API void asAcquireSharedLock(); + AS_API void asReleaseSharedLock(); + AS_API int asAtomicInc(int &value); + AS_API int asAtomicDec(int &value); + AS_API int asThreadCleanup(); + + // Memory management + AS_API int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); + AS_API int asResetGlobalMemoryFunctions(); + AS_API void *asAllocMem(size_t size); + AS_API void asFreeMem(void *mem); + + // Auxiliary + AS_API asILockableSharedBool *asCreateLockableSharedBool(); +} +#endif // ANGELSCRIPT_DLL_MANUAL_IMPORT + +// Determine traits of a type for registration of value types +// Relies on C++11 features so it can not be used with non-compliant compilers +#ifdef AS_CAN_USE_CPP11 + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +template +asUINT asGetTypeTraits() +{ +#if defined(_MSC_VER) || defined(_LIBCPP_TYPE_TRAITS) || (__GNUC__ >= 5) || defined(__clang__) + // MSVC, XCode/Clang, and gnuc 5+ + // C++11 compliant code + bool hasConstructor = std::is_default_constructible::value && !std::is_trivially_default_constructible::value; + bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::is_trivially_copy_assignable::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::is_trivially_copy_constructible::value; +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + // gnuc 4.8 is using a mix of C++11 standard and pre-standard templates + bool hasConstructor = std::is_default_constructible::value && !std::has_trivial_default_constructor::value; + bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::has_trivial_copy_assign::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::has_trivial_copy_constructor::value; +#else + // All other compilers and versions are assumed to use non C++11 compliant code until proven otherwise + // Not fully C++11 compliant. The has_trivial checks were used while the standard was still + // being elaborated, but were then removed in favor of the above is_trivially checks + // http://stackoverflow.com/questions/12702103/writing-code-that-works-when-has-trivial-destructor-is-defined-instead-of-is + // https://github.com/mozart/mozart2/issues/51 + bool hasConstructor = std::is_default_constructible::value && !std::has_trivial_default_constructor::value; + bool hasDestructor = std::is_destructible::value && !std::has_trivial_destructor::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::has_trivial_copy_assign::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::has_trivial_copy_constructor::value; +#endif + bool isFloat = std::is_floating_point::value; + bool isPrimitive = std::is_integral::value || std::is_pointer::value || std::is_enum::value; + bool isClass = std::is_class::value; + bool isArray = std::is_array::value; + + if( isFloat ) + return asOBJ_APP_FLOAT; + if( isPrimitive ) + return asOBJ_APP_PRIMITIVE; + + if( isClass ) + { + asDWORD flags = asOBJ_APP_CLASS; + if( hasConstructor ) + flags |= asOBJ_APP_CLASS_CONSTRUCTOR; + if( hasDestructor ) + flags |= asOBJ_APP_CLASS_DESTRUCTOR; + if( hasAssignmentOperator ) + flags |= asOBJ_APP_CLASS_ASSIGNMENT; + if( hasCopyConstructor ) + flags |= asOBJ_APP_CLASS_COPY_CONSTRUCTOR; + return flags; + } + + if( isArray ) + return asOBJ_APP_ARRAY; + + // Unknown type traits + return 0; +} + +#endif // c++11 + +// Interface declarations + +class asIScriptEngine +{ +public: + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + virtual int ShutDownAndRelease() = 0; + + // Engine properties + virtual int SetEngineProperty(asEEngineProp property, asPWORD value) = 0; + virtual asPWORD GetEngineProperty(asEEngineProp property) const = 0; + + // Compiler messages + virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) = 0; + virtual int ClearMessageCallback() = 0; + virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) = 0; + + // JIT Compiler + virtual int SetJITCompiler(asIJITCompiler *compiler) = 0; + virtual asIJITCompiler *GetJITCompiler() const = 0; + + // Global functions + virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0) = 0; + virtual asUINT GetGlobalFunctionCount() const = 0; + virtual asIScriptFunction *GetGlobalFunctionByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetGlobalFunctionByDecl(const char *declaration) const = 0; + + // Global properties + virtual int RegisterGlobalProperty(const char *declaration, void *pointer) = 0; + virtual asUINT GetGlobalPropertyCount() const = 0; + virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0, asDWORD *accessMask = 0) const = 0; + virtual int GetGlobalPropertyIndexByName(const char *name) const = 0; + virtual int GetGlobalPropertyIndexByDecl(const char *decl) const = 0; + + // Object types + virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags) = 0; + virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) = 0; + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0) = 0; + virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0) = 0; + virtual int RegisterInterface(const char *name) = 0; + virtual int RegisterInterfaceMethod(const char *intf, const char *declaration) = 0; + virtual asUINT GetObjectTypeCount() const = 0; + virtual asITypeInfo *GetObjectTypeByIndex(asUINT index) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeByName(const char *name) const = 0; + virtual asITypeInfo *GetObjectTypeByDecl(const char *decl) const = 0; +#endif + + // String factory + virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv, void *auxiliary = 0) = 0; + virtual int GetStringFactoryReturnTypeId(asDWORD *flags = 0) const = 0; + + // Default array type + virtual int RegisterDefaultArrayType(const char *type) = 0; + virtual int GetDefaultArrayTypeId() const = 0; + + // Enums + virtual int RegisterEnum(const char *type) = 0; + virtual int RegisterEnumValue(const char *type, const char *name, int value) = 0; + virtual asUINT GetEnumCount() const = 0; + virtual asITypeInfo *GetEnumByIndex(asUINT index) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual int GetEnumValueCount(int enumTypeId) const = 0; + virtual const char * GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; +#endif + + // Funcdefs + virtual int RegisterFuncdef(const char *decl) = 0; + virtual asUINT GetFuncdefCount() const = 0; + virtual asITypeInfo *GetFuncdefByIndex(asUINT index) const = 0; + + // Typedefs + virtual int RegisterTypedef(const char *type, const char *decl) = 0; + virtual asUINT GetTypedefCount() const = 0; + virtual asITypeInfo *GetTypedefByIndex(asUINT index) const = 0; + + // Configuration groups + virtual int BeginConfigGroup(const char *groupName) = 0; + virtual int EndConfigGroup() = 0; + virtual int RemoveConfigGroup(const char *groupName) = 0; + virtual asDWORD SetDefaultAccessMask(asDWORD defaultMask) = 0; + virtual int SetDefaultNamespace(const char *nameSpace) = 0; + virtual const char *GetDefaultNamespace() const = 0; + + // Script modules + virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag = asGM_ONLY_IF_EXISTS) = 0; + virtual int DiscardModule(const char *module) = 0; + virtual asUINT GetModuleCount() const = 0; + virtual asIScriptModule *GetModuleByIndex(asUINT index) const = 0; + + // Script functions + virtual asIScriptFunction *GetFunctionById(int funcId) const = 0; +#ifdef AS_DEPRECATED + // deprecated since 2.31.0, 2016-01-01 + virtual asIScriptFunction *GetFuncdefFromTypeId(int typeId) const = 0; +#endif + + // Type identification +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeById(int typeId) const = 0; +#endif + virtual int GetTypeIdByDecl(const char *decl) const = 0; + virtual const char *GetTypeDeclaration(int typeId, bool includeNamespace = false) const = 0; + virtual int GetSizeOfPrimitiveType(int typeId) const = 0; + virtual asITypeInfo *GetTypeInfoById(int typeId) const = 0; + virtual asITypeInfo *GetTypeInfoByName(const char *name) const = 0; + virtual asITypeInfo *GetTypeInfoByDecl(const char *decl) const = 0; + + // Script execution + virtual asIScriptContext *CreateContext() = 0; + virtual void *CreateScriptObject(const asITypeInfo *type) = 0; + virtual void *CreateScriptObjectCopy(void *obj, const asITypeInfo *type) = 0; + virtual void *CreateUninitializedScriptObject(const asITypeInfo *type) = 0; + virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj) = 0; + virtual int AssignScriptObject(void *dstObj, void *srcObj, const asITypeInfo *type) = 0; + virtual void ReleaseScriptObject(void *obj, const asITypeInfo *type) = 0; + virtual void AddRefScriptObject(void *obj, const asITypeInfo *type) = 0; + virtual int RefCastObject(void *obj, asITypeInfo *fromType, asITypeInfo *toType, void **newPtr, bool useOnlyImplicitCast = false) = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0, 2014-11-04 + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const = 0; +#endif + virtual asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asITypeInfo *type) const = 0; + + // Context pooling + virtual asIScriptContext *RequestContext() = 0; + virtual void ReturnContext(asIScriptContext *ctx) = 0; + virtual int SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param = 0) = 0; + + // String interpretation + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, asUINT *tokenLength = 0) const = 0; + + // Garbage collection + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE, asUINT numIterations = 1) = 0; + virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed = 0, asUINT *totalDetected = 0, asUINT *newObjects = 0, asUINT *totalNewDestroyed = 0) const = 0; + virtual int NotifyGarbageCollectorOfNewObject(void *obj, asITypeInfo *type) = 0; + virtual int GetObjectInGC(asUINT idx, asUINT *seqNbr = 0, void **obj = 0, asITypeInfo **type = 0) = 0; + virtual void GCEnumCallback(void *reference) = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type = 0) = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual void SetObjectTypeUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type = 0) = 0; +#endif + virtual void SetTypeInfoUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type = 0) = 0; + +protected: + virtual ~asIScriptEngine() {} +}; + +class asIThreadManager +{ +protected: + virtual ~asIThreadManager() {} +}; + +class asIScriptModule +{ +public: + virtual asIScriptEngine *GetEngine() const = 0; + virtual void SetName(const char *name) = 0; + virtual const char *GetName() const = 0; + virtual void Discard() = 0; + + // Compilation + virtual int AddScriptSection(const char *name, const char *code, size_t codeLength = 0, int lineOffset = 0) = 0; + virtual int Build() = 0; + virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) = 0; + virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) = 0; + virtual asDWORD SetAccessMask(asDWORD accessMask) = 0; + virtual int SetDefaultNamespace(const char *nameSpace) = 0; + virtual const char *GetDefaultNamespace() const = 0; + + // Functions + virtual asUINT GetFunctionCount() const = 0; + virtual asIScriptFunction *GetFunctionByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetFunctionByDecl(const char *decl) const = 0; + virtual asIScriptFunction *GetFunctionByName(const char *name) const = 0; + virtual int RemoveFunction(asIScriptFunction *func) = 0; + + // Global variables + virtual int ResetGlobalVars(asIScriptContext *ctx = 0) = 0; + virtual asUINT GetGlobalVarCount() const = 0; + virtual int GetGlobalVarIndexByName(const char *name) const = 0; + virtual int GetGlobalVarIndexByDecl(const char *decl) const = 0; + virtual const char *GetGlobalVarDeclaration(asUINT index, bool includeNamespace = false) const = 0; + virtual int GetGlobalVar(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0) const = 0; + virtual void *GetAddressOfGlobalVar(asUINT index) = 0; + virtual int RemoveGlobalVar(asUINT index) = 0; + + // Type identification + virtual asUINT GetObjectTypeCount() const = 0; + virtual asITypeInfo *GetObjectTypeByIndex(asUINT index) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeByName(const char *name) const = 0; + virtual asITypeInfo *GetObjectTypeByDecl(const char *decl) const = 0; +#endif + virtual int GetTypeIdByDecl(const char *decl) const = 0; + virtual asITypeInfo *GetTypeInfoByName(const char *name) const = 0; + virtual asITypeInfo *GetTypeInfoByDecl(const char *decl) const = 0; + + // Enums + virtual asUINT GetEnumCount() const = 0; + virtual asITypeInfo *GetEnumByIndex(asUINT index) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual int GetEnumValueCount(int enumTypeId) const = 0; + virtual const char * GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; +#endif + + // Typedefs + virtual asUINT GetTypedefCount() const = 0; + virtual asITypeInfo *GetTypedefByIndex(asUINT index) const = 0; + + // Dynamic binding between modules + virtual asUINT GetImportedFunctionCount() const = 0; + virtual int GetImportedFunctionIndexByDecl(const char *decl) const = 0; + virtual const char *GetImportedFunctionDeclaration(asUINT importIndex) const = 0; + virtual const char *GetImportedFunctionSourceModule(asUINT importIndex) const = 0; + virtual int BindImportedFunction(asUINT importIndex, asIScriptFunction *func) = 0; + virtual int UnbindImportedFunction(asUINT importIndex) = 0; + virtual int BindAllImportedFunctions() = 0; + virtual int UnbindAllImportedFunctions() = 0; + + // Byte code saving and loading + virtual int SaveByteCode(asIBinaryStream *out, bool stripDebugInfo = false) const = 0; + virtual int LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped = 0) = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + +protected: + virtual ~asIScriptModule() {} +}; + +class asIScriptContext +{ +public: + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + + // Execution + virtual int Prepare(asIScriptFunction *func) = 0; + virtual int Unprepare() = 0; + virtual int Execute() = 0; + virtual int Abort() = 0; + virtual int Suspend() = 0; + virtual asEContextState GetState() const = 0; + virtual int PushState() = 0; + virtual int PopState() = 0; + virtual bool IsNested(asUINT *nestCount = 0) const = 0; + + // Object pointer for calling class methods + virtual int SetObject(void *obj) = 0; + + // Arguments + virtual int SetArgByte(asUINT arg, asBYTE value) = 0; + virtual int SetArgWord(asUINT arg, asWORD value) = 0; + virtual int SetArgDWord(asUINT arg, asDWORD value) = 0; + virtual int SetArgQWord(asUINT arg, asQWORD value) = 0; + virtual int SetArgFloat(asUINT arg, float value) = 0; + virtual int SetArgDouble(asUINT arg, double value) = 0; + virtual int SetArgAddress(asUINT arg, void *addr) = 0; + virtual int SetArgObject(asUINT arg, void *obj) = 0; + virtual int SetArgVarType(asUINT arg, void *ptr, int typeId) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; + + // Return value + virtual asBYTE GetReturnByte() = 0; + virtual asWORD GetReturnWord() = 0; + virtual asDWORD GetReturnDWord() = 0; + virtual asQWORD GetReturnQWord() = 0; + virtual float GetReturnFloat() = 0; + virtual double GetReturnDouble() = 0; + virtual void *GetReturnAddress() = 0; + virtual void *GetReturnObject() = 0; + virtual void *GetAddressOfReturnValue() = 0; + + // Exception handling + virtual int SetException(const char *string) = 0; + virtual int GetExceptionLineNumber(int *column = 0, const char **sectionName = 0) = 0; + virtual asIScriptFunction *GetExceptionFunction() = 0; + virtual const char * GetExceptionString() = 0; + virtual int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearExceptionCallback() = 0; + + // Debugging + virtual int SetLineCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearLineCallback() = 0; + virtual asUINT GetCallstackSize() const = 0; + virtual asIScriptFunction *GetFunction(asUINT stackLevel = 0) = 0; + virtual int GetLineNumber(asUINT stackLevel = 0, int *column = 0, const char **sectionName = 0) = 0; + virtual int GetVarCount(asUINT stackLevel = 0) = 0; + virtual const char *GetVarName(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual const char *GetVarDeclaration(asUINT varIndex, asUINT stackLevel = 0, bool includeNamespace = false) = 0; + virtual int GetVarTypeId(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual void *GetAddressOfVar(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual bool IsVarInScope(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual int GetThisTypeId(asUINT stackLevel = 0) = 0; + virtual void *GetThisPointer(asUINT stackLevel = 0) = 0; + virtual asIScriptFunction *GetSystemFunction() = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + +protected: + virtual ~asIScriptContext() {} +}; + +class asIScriptGeneric +{ +public: + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual asIScriptFunction *GetFunction() const = 0; + virtual void *GetAuxiliary() const = 0; + + // Object + virtual void *GetObject() = 0; + virtual int GetObjectTypeId() const = 0; + + // Arguments + virtual int GetArgCount() const = 0; + virtual int GetArgTypeId(asUINT arg, asDWORD *flags = 0) const = 0; + virtual asBYTE GetArgByte(asUINT arg) = 0; + virtual asWORD GetArgWord(asUINT arg) = 0; + virtual asDWORD GetArgDWord(asUINT arg) = 0; + virtual asQWORD GetArgQWord(asUINT arg) = 0; + virtual float GetArgFloat(asUINT arg) = 0; + virtual double GetArgDouble(asUINT arg) = 0; + virtual void *GetArgAddress(asUINT arg) = 0; + virtual void *GetArgObject(asUINT arg) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; + + // Return value + virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; + virtual int SetReturnByte(asBYTE val) = 0; + virtual int SetReturnWord(asWORD val) = 0; + virtual int SetReturnDWord(asDWORD val) = 0; + virtual int SetReturnQWord(asQWORD val) = 0; + virtual int SetReturnFloat(float val) = 0; + virtual int SetReturnDouble(double val) = 0; + virtual int SetReturnAddress(void *addr) = 0; + virtual int SetReturnObject(void *obj) = 0; + virtual void *GetAddressOfReturnLocation() = 0; + +protected: + virtual ~asIScriptGeneric() {} +}; + +class asIScriptObject +{ +public: + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + virtual asILockableSharedBool *GetWeakRefFlag() const = 0; + + // Type info + virtual int GetTypeId() const = 0; + virtual asITypeInfo *GetObjectType() const = 0; + + // Class properties + virtual asUINT GetPropertyCount() const = 0; + virtual int GetPropertyTypeId(asUINT prop) const = 0; + virtual const char *GetPropertyName(asUINT prop) const = 0; + virtual void *GetAddressOfProperty(asUINT prop) = 0; + + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual int CopyFrom(asIScriptObject *other) = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + +protected: + virtual ~asIScriptObject() {} +}; + +class asITypeInfo +{ +public: + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual const char *GetConfigGroup() const = 0; + virtual asDWORD GetAccessMask() const = 0; + virtual asIScriptModule *GetModule() const = 0; + + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + + // Type info + virtual const char *GetName() const = 0; + virtual const char *GetNamespace() const = 0; + virtual asITypeInfo *GetBaseType() const = 0; + virtual bool DerivesFrom(const asITypeInfo *objType) const = 0; + virtual asDWORD GetFlags() const = 0; + virtual asUINT GetSize() const = 0; + virtual int GetTypeId() const = 0; + virtual int GetSubTypeId(asUINT subTypeIndex = 0) const = 0; + virtual asITypeInfo *GetSubType(asUINT subTypeIndex = 0) const = 0; + virtual asUINT GetSubTypeCount() const = 0; + + // Interfaces + virtual asUINT GetInterfaceCount() const = 0; + virtual asITypeInfo *GetInterface(asUINT index) const = 0; + virtual bool Implements(const asITypeInfo *objType) const = 0; + + // Factories + virtual asUINT GetFactoryCount() const = 0; + virtual asIScriptFunction *GetFactoryByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetFactoryByDecl(const char *decl) const = 0; + + // Methods + virtual asUINT GetMethodCount() const = 0; + virtual asIScriptFunction *GetMethodByIndex(asUINT index, bool getVirtual = true) const = 0; + virtual asIScriptFunction *GetMethodByName(const char *name, bool getVirtual = true) const = 0; + virtual asIScriptFunction *GetMethodByDecl(const char *decl, bool getVirtual = true) const = 0; + + // Properties + virtual asUINT GetPropertyCount() const = 0; + virtual int GetProperty(asUINT index, const char **name, int *typeId = 0, bool *isPrivate = 0, bool *isProtected = 0, int *offset = 0, bool *isReference = 0, asDWORD *accessMask = 0) const = 0; + virtual const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const = 0; + + // Behaviours + virtual asUINT GetBehaviourCount() const = 0; + virtual asIScriptFunction *GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const = 0; + + // Child types + virtual asUINT GetChildFuncdefCount() const = 0; + virtual asITypeInfo *GetChildFuncdef(asUINT index) const = 0; + virtual asITypeInfo *GetParentType() const = 0; + + // Enums + virtual asUINT GetEnumValueCount() const = 0; + virtual const char *GetEnumValueByIndex(asUINT index, int *outValue) const = 0; + + // Typedef + virtual int GetTypedefTypeId() const = 0; + + // Funcdef + virtual asIScriptFunction *GetFuncdefSignature() const = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + +protected: + virtual ~asITypeInfo() {} +}; + +class asIScriptFunction +{ +public: + virtual asIScriptEngine *GetEngine() const = 0; + + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + + // Miscellaneous + virtual int GetId() const = 0; + virtual asEFuncType GetFuncType() const = 0; + virtual const char *GetModuleName() const = 0; + virtual asIScriptModule *GetModule() const = 0; + virtual const char *GetScriptSectionName() const = 0; + virtual const char *GetConfigGroup() const = 0; + virtual asDWORD GetAccessMask() const = 0; + virtual void *GetAuxiliary() const = 0; + + // Function signature + virtual asITypeInfo *GetObjectType() const = 0; + virtual const char *GetObjectName() const = 0; + virtual const char *GetName() const = 0; + virtual const char *GetNamespace() const = 0; + virtual const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const = 0; + virtual bool IsReadOnly() const = 0; + virtual bool IsPrivate() const = 0; + virtual bool IsProtected() const = 0; + virtual bool IsFinal() const = 0; + virtual bool IsOverride() const = 0; + virtual bool IsShared() const = 0; + virtual asUINT GetParamCount() const = 0; + virtual int GetParam(asUINT index, int *typeId, asDWORD *flags = 0, const char **name = 0, const char **defaultArg = 0) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.29.0, 2014-04-06 + virtual int GetParamTypeId(asUINT index, asDWORD *flags = 0) const = 0; +#endif + virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; + + // Type id for function pointers + virtual int GetTypeId() const = 0; + virtual bool IsCompatibleWithTypeId(int typeId) const = 0; + + // Delegates + virtual void *GetDelegateObject() const = 0; + virtual asITypeInfo *GetDelegateObjectType() const = 0; + virtual asIScriptFunction *GetDelegateFunction() const = 0; + + // Debug information + virtual asUINT GetVarCount() const = 0; + virtual int GetVar(asUINT index, const char **name, int *typeId = 0) const = 0; + virtual const char *GetVarDecl(asUINT index, bool includeNamespace = false) const = 0; + virtual int FindNextLineWithCode(int line) const = 0; + + // For JIT compilation + virtual asDWORD *GetByteCode(asUINT *length = 0) = 0; + + // User data + virtual void *SetUserData(void *userData, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + +protected: + virtual ~asIScriptFunction() {}; +}; + +class asIBinaryStream +{ +public: + virtual void Read(void *ptr, asUINT size) = 0; + virtual void Write(const void *ptr, asUINT size) = 0; + +public: + virtual ~asIBinaryStream() {} +}; + +class asILockableSharedBool +{ +public: + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + + // Value + virtual bool Get() const = 0; + virtual void Set(bool val) = 0; + + // Thread management + virtual void Lock() const = 0; + virtual void Unlock() const = 0; + +protected: + virtual ~asILockableSharedBool() {} +}; + +//----------------------------------------------------------------- +// Function pointers + +// Template function to capture all global functions, +// except the ones using the generic calling convention +template +inline asSFuncPtr asFunctionPtr(T func) +{ + // Mark this as a global function + asSFuncPtr p(2); + +#ifdef AS_64BIT_PTR + // The size_t cast is to avoid a compiler warning with asFUNCTION(0) + // on 64bit, as 0 is interpreted as a 32bit int value + p.ptr.f.func = reinterpret_cast(size_t(func)); +#else + // MSVC6 doesn't like the size_t cast above so I + // solved this with a separate code for 32bit. + p.ptr.f.func = reinterpret_cast(func); +#endif + + return p; +} + +// Specialization for functions using the generic calling convention +template<> +inline asSFuncPtr asFunctionPtr(asGENFUNC_t func) +{ + // Mark this as a generic function + asSFuncPtr p(1); + p.ptr.f.func = reinterpret_cast(func); + return p; +} + +#ifndef AS_NO_CLASS_METHODS + +// Method pointers + +// Declare a dummy class so that we can determine the size of a simple method pointer +class asCSimpleDummy {}; +typedef void (asCSimpleDummy::*asSIMPLEMETHOD_t)(); +const int SINGLE_PTR_SIZE = sizeof(asSIMPLEMETHOD_t); + +// Define template +template +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // This version of the function should never be executed, nor compiled, + // as it would mean that the size of the method pointer cannot be determined. + + int ERROR_UnsupportedMethodPtr[N-100]; + + asSFuncPtr p(0); + return p; + } +}; + +// Template specialization +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE); + return p; + } +}; + +#if defined(_MSC_VER) && !defined(__MWERKS__) + +// MSVC and Intel uses different sizes for different class method pointers +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+sizeof(int)); + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // On 32bit platforms with is where a class with virtual inheritance falls. + // On 64bit platforms we can also fall here if 8byte data alignments is used. + + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+2*sizeof(int)); + + // Microsoft has a terrible optimization on class methods with virtual inheritance. + // They are hardcoding an important offset, which is not coming in the method pointer. + +#if defined(_MSC_VER) && !defined(AS_64BIT_PTR) + // Method pointers for virtual inheritance is not supported, + // as it requires the location of the vbase table, which is + // only available to the C++ compiler, but not in the method + // pointer. + + // You can get around this by forward declaring the class and + // storing the sizeof its method pointer in a constant. Example: + + // class ClassWithVirtualInheritance; + // const int ClassWithVirtualInheritance_workaround = sizeof(void ClassWithVirtualInheritance::*()); + + // This will force the compiler to use the unknown type + // for the class, which falls under the next case + + + // Copy the virtual table index to the 4th dword so that AngelScript + // can properly detect and deny the use of methods with virtual inheritance. + *(reinterpret_cast(&p)+3) = *(reinterpret_cast(&p)+2); +#endif + + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+3*sizeof(int)); + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // On 64bit platforms with 8byte data alignment + // the unknown class method pointers will come here. + + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+4*sizeof(int)); + return p; + } +}; + +#endif + +#endif // AS_NO_CLASS_METHODS + +//---------------------------------------------------------------- +// JIT compiler + +struct asSVMRegisters +{ + asDWORD *programPointer; // points to current bytecode instruction + asDWORD *stackFramePointer; // function stack frame + asDWORD *stackPointer; // top of stack (grows downward) + asQWORD valueRegister; // temp register for primitives + void *objectRegister; // temp register for objects and handles + asITypeInfo *objectType; // type of object held in object register + bool doProcessSuspend; // whether or not the JIT should break out when it encounters a suspend instruction + asIScriptContext *ctx; // the active context +}; + +typedef void (*asJITFunction)(asSVMRegisters *registers, asPWORD jitArg); + +class asIJITCompiler +{ +public: + virtual int CompileFunction(asIScriptFunction *function, asJITFunction *output) = 0; + virtual void ReleaseJITFunction(asJITFunction func) = 0; +public: + virtual ~asIJITCompiler() {} +}; + +// Byte code instructions +enum asEBCInstr +{ + asBC_PopPtr = 0, + asBC_PshGPtr = 1, + asBC_PshC4 = 2, + asBC_PshV4 = 3, + asBC_PSF = 4, + asBC_SwapPtr = 5, + asBC_NOT = 6, + asBC_PshG4 = 7, + asBC_LdGRdR4 = 8, + asBC_CALL = 9, + asBC_RET = 10, + asBC_JMP = 11, + asBC_JZ = 12, + asBC_JNZ = 13, + asBC_JS = 14, + asBC_JNS = 15, + asBC_JP = 16, + asBC_JNP = 17, + asBC_TZ = 18, + asBC_TNZ = 19, + asBC_TS = 20, + asBC_TNS = 21, + asBC_TP = 22, + asBC_TNP = 23, + asBC_NEGi = 24, + asBC_NEGf = 25, + asBC_NEGd = 26, + asBC_INCi16 = 27, + asBC_INCi8 = 28, + asBC_DECi16 = 29, + asBC_DECi8 = 30, + asBC_INCi = 31, + asBC_DECi = 32, + asBC_INCf = 33, + asBC_DECf = 34, + asBC_INCd = 35, + asBC_DECd = 36, + asBC_IncVi = 37, + asBC_DecVi = 38, + asBC_BNOT = 39, + asBC_BAND = 40, + asBC_BOR = 41, + asBC_BXOR = 42, + asBC_BSLL = 43, + asBC_BSRL = 44, + asBC_BSRA = 45, + asBC_COPY = 46, + asBC_PshC8 = 47, + asBC_PshVPtr = 48, + asBC_RDSPtr = 49, + asBC_CMPd = 50, + asBC_CMPu = 51, + asBC_CMPf = 52, + asBC_CMPi = 53, + asBC_CMPIi = 54, + asBC_CMPIf = 55, + asBC_CMPIu = 56, + asBC_JMPP = 57, + asBC_PopRPtr = 58, + asBC_PshRPtr = 59, + asBC_STR = 60, + asBC_CALLSYS = 61, + asBC_CALLBND = 62, + asBC_SUSPEND = 63, + asBC_ALLOC = 64, + asBC_FREE = 65, + asBC_LOADOBJ = 66, + asBC_STOREOBJ = 67, + asBC_GETOBJ = 68, + asBC_REFCPY = 69, + asBC_CHKREF = 70, + asBC_GETOBJREF = 71, + asBC_GETREF = 72, + asBC_PshNull = 73, + asBC_ClrVPtr = 74, + asBC_OBJTYPE = 75, + asBC_TYPEID = 76, + asBC_SetV4 = 77, + asBC_SetV8 = 78, + asBC_ADDSi = 79, + asBC_CpyVtoV4 = 80, + asBC_CpyVtoV8 = 81, + asBC_CpyVtoR4 = 82, + asBC_CpyVtoR8 = 83, + asBC_CpyVtoG4 = 84, + asBC_CpyRtoV4 = 85, + asBC_CpyRtoV8 = 86, + asBC_CpyGtoV4 = 87, + asBC_WRTV1 = 88, + asBC_WRTV2 = 89, + asBC_WRTV4 = 90, + asBC_WRTV8 = 91, + asBC_RDR1 = 92, + asBC_RDR2 = 93, + asBC_RDR4 = 94, + asBC_RDR8 = 95, + asBC_LDG = 96, + asBC_LDV = 97, + asBC_PGA = 98, + asBC_CmpPtr = 99, + asBC_VAR = 100, + asBC_iTOf = 101, + asBC_fTOi = 102, + asBC_uTOf = 103, + asBC_fTOu = 104, + asBC_sbTOi = 105, + asBC_swTOi = 106, + asBC_ubTOi = 107, + asBC_uwTOi = 108, + asBC_dTOi = 109, + asBC_dTOu = 110, + asBC_dTOf = 111, + asBC_iTOd = 112, + asBC_uTOd = 113, + asBC_fTOd = 114, + asBC_ADDi = 115, + asBC_SUBi = 116, + asBC_MULi = 117, + asBC_DIVi = 118, + asBC_MODi = 119, + asBC_ADDf = 120, + asBC_SUBf = 121, + asBC_MULf = 122, + asBC_DIVf = 123, + asBC_MODf = 124, + asBC_ADDd = 125, + asBC_SUBd = 126, + asBC_MULd = 127, + asBC_DIVd = 128, + asBC_MODd = 129, + asBC_ADDIi = 130, + asBC_SUBIi = 131, + asBC_MULIi = 132, + asBC_ADDIf = 133, + asBC_SUBIf = 134, + asBC_MULIf = 135, + asBC_SetG4 = 136, + asBC_ChkRefS = 137, + asBC_ChkNullV = 138, + asBC_CALLINTF = 139, + asBC_iTOb = 140, + asBC_iTOw = 141, + asBC_SetV1 = 142, + asBC_SetV2 = 143, + asBC_Cast = 144, + asBC_i64TOi = 145, + asBC_uTOi64 = 146, + asBC_iTOi64 = 147, + asBC_fTOi64 = 148, + asBC_dTOi64 = 149, + asBC_fTOu64 = 150, + asBC_dTOu64 = 151, + asBC_i64TOf = 152, + asBC_u64TOf = 153, + asBC_i64TOd = 154, + asBC_u64TOd = 155, + asBC_NEGi64 = 156, + asBC_INCi64 = 157, + asBC_DECi64 = 158, + asBC_BNOT64 = 159, + asBC_ADDi64 = 160, + asBC_SUBi64 = 161, + asBC_MULi64 = 162, + asBC_DIVi64 = 163, + asBC_MODi64 = 164, + asBC_BAND64 = 165, + asBC_BOR64 = 166, + asBC_BXOR64 = 167, + asBC_BSLL64 = 168, + asBC_BSRL64 = 169, + asBC_BSRA64 = 170, + asBC_CMPi64 = 171, + asBC_CMPu64 = 172, + asBC_ChkNullS = 173, + asBC_ClrHi = 174, + asBC_JitEntry = 175, + asBC_CallPtr = 176, + asBC_FuncPtr = 177, + asBC_LoadThisR = 178, + asBC_PshV8 = 179, + asBC_DIVu = 180, + asBC_MODu = 181, + asBC_DIVu64 = 182, + asBC_MODu64 = 183, + asBC_LoadRObjR = 184, + asBC_LoadVObjR = 185, + asBC_RefCpyV = 186, + asBC_JLowZ = 187, + asBC_JLowNZ = 188, + asBC_AllocMem = 189, + asBC_SetListSize = 190, + asBC_PshListElmnt = 191, + asBC_SetListType = 192, + asBC_POWi = 193, + asBC_POWu = 194, + asBC_POWf = 195, + asBC_POWd = 196, + asBC_POWdi = 197, + asBC_POWi64 = 198, + asBC_POWu64 = 199, + asBC_Thiscall1 = 200, + asBC_MAXBYTECODE = 201, + + // Temporary tokens. Can't be output to the final program + asBC_VarDecl = 251, + asBC_Block = 252, + asBC_ObjInfo = 253, + asBC_LINE = 254, + asBC_LABEL = 255 +}; + +// Instruction types +enum asEBCType +{ + asBCTYPE_INFO = 0, + asBCTYPE_NO_ARG = 1, + asBCTYPE_W_ARG = 2, + asBCTYPE_wW_ARG = 3, + asBCTYPE_DW_ARG = 4, + asBCTYPE_rW_DW_ARG = 5, + asBCTYPE_QW_ARG = 6, + asBCTYPE_DW_DW_ARG = 7, + asBCTYPE_wW_rW_rW_ARG = 8, + asBCTYPE_wW_QW_ARG = 9, + asBCTYPE_wW_rW_ARG = 10, + asBCTYPE_rW_ARG = 11, + asBCTYPE_wW_DW_ARG = 12, + asBCTYPE_wW_rW_DW_ARG = 13, + asBCTYPE_rW_rW_ARG = 14, + asBCTYPE_wW_W_ARG = 15, + asBCTYPE_QW_DW_ARG = 16, + asBCTYPE_rW_QW_ARG = 17, + asBCTYPE_W_DW_ARG = 18, + asBCTYPE_rW_W_DW_ARG = 19, + asBCTYPE_rW_DW_DW_ARG = 20 +}; + +// Instruction type sizes +const int asBCTypeSize[21] = +{ + 0, // asBCTYPE_INFO + 1, // asBCTYPE_NO_ARG + 1, // asBCTYPE_W_ARG + 1, // asBCTYPE_wW_ARG + 2, // asBCTYPE_DW_ARG + 2, // asBCTYPE_rW_DW_ARG + 3, // asBCTYPE_QW_ARG + 3, // asBCTYPE_DW_DW_ARG + 2, // asBCTYPE_wW_rW_rW_ARG + 3, // asBCTYPE_wW_QW_ARG + 2, // asBCTYPE_wW_rW_ARG + 1, // asBCTYPE_rW_ARG + 2, // asBCTYPE_wW_DW_ARG + 3, // asBCTYPE_wW_rW_DW_ARG + 2, // asBCTYPE_rW_rW_ARG + 2, // asBCTYPE_wW_W_ARG + 4, // asBCTYPE_QW_DW_ARG + 3, // asBCTYPE_rW_QW_ARG + 2, // asBCTYPE_W_DW_ARG + 3, // asBCTYPE_rW_W_DW_ARG + 3 // asBCTYPE_rW_DW_DW_ARG +}; + +// Instruction info +struct asSBCInfo +{ + asEBCInstr bc; + asEBCType type; + int stackInc; + const char *name; +}; + +#ifndef AS_64BIT_PTR + #define asBCTYPE_PTR_ARG asBCTYPE_DW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_DW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_DW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_DW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 1 + #endif +#else + #define asBCTYPE_PTR_ARG asBCTYPE_QW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_QW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_QW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_QW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 2 + #endif +#endif + +#define asBCINFO(b,t,s) {asBC_##b, asBCTYPE_##t, s, #b} +#define asBCINFO_DUMMY(b) {asBC_MAXBYTECODE, asBCTYPE_INFO, 0, "BC_" #b} + +const asSBCInfo asBCInfo[256] = +{ + asBCINFO(PopPtr, NO_ARG, -AS_PTR_SIZE), + asBCINFO(PshGPtr, PTR_ARG, AS_PTR_SIZE), + asBCINFO(PshC4, DW_ARG, 1), + asBCINFO(PshV4, rW_ARG, 1), + asBCINFO(PSF, rW_ARG, AS_PTR_SIZE), + asBCINFO(SwapPtr, NO_ARG, 0), + asBCINFO(NOT, rW_ARG, 0), + asBCINFO(PshG4, PTR_ARG, 1), + asBCINFO(LdGRdR4, wW_PTR_ARG, 0), + asBCINFO(CALL, DW_ARG, 0xFFFF), + asBCINFO(RET, W_ARG, 0xFFFF), + asBCINFO(JMP, DW_ARG, 0), + asBCINFO(JZ, DW_ARG, 0), + asBCINFO(JNZ, DW_ARG, 0), + asBCINFO(JS, DW_ARG, 0), + asBCINFO(JNS, DW_ARG, 0), + asBCINFO(JP, DW_ARG, 0), + asBCINFO(JNP, DW_ARG, 0), + asBCINFO(TZ, NO_ARG, 0), + asBCINFO(TNZ, NO_ARG, 0), + asBCINFO(TS, NO_ARG, 0), + asBCINFO(TNS, NO_ARG, 0), + asBCINFO(TP, NO_ARG, 0), + asBCINFO(TNP, NO_ARG, 0), + asBCINFO(NEGi, rW_ARG, 0), + asBCINFO(NEGf, rW_ARG, 0), + asBCINFO(NEGd, rW_ARG, 0), + asBCINFO(INCi16, NO_ARG, 0), + asBCINFO(INCi8, NO_ARG, 0), + asBCINFO(DECi16, NO_ARG, 0), + asBCINFO(DECi8, NO_ARG, 0), + asBCINFO(INCi, NO_ARG, 0), + asBCINFO(DECi, NO_ARG, 0), + asBCINFO(INCf, NO_ARG, 0), + asBCINFO(DECf, NO_ARG, 0), + asBCINFO(INCd, NO_ARG, 0), + asBCINFO(DECd, NO_ARG, 0), + asBCINFO(IncVi, rW_ARG, 0), + asBCINFO(DecVi, rW_ARG, 0), + asBCINFO(BNOT, rW_ARG, 0), + asBCINFO(BAND, wW_rW_rW_ARG, 0), + asBCINFO(BOR, wW_rW_rW_ARG, 0), + asBCINFO(BXOR, wW_rW_rW_ARG, 0), + asBCINFO(BSLL, wW_rW_rW_ARG, 0), + asBCINFO(BSRL, wW_rW_rW_ARG, 0), + asBCINFO(BSRA, wW_rW_rW_ARG, 0), + asBCINFO(COPY, W_DW_ARG, -AS_PTR_SIZE), + asBCINFO(PshC8, QW_ARG, 2), + asBCINFO(PshVPtr, rW_ARG, AS_PTR_SIZE), + asBCINFO(RDSPtr, NO_ARG, 0), + asBCINFO(CMPd, rW_rW_ARG, 0), + asBCINFO(CMPu, rW_rW_ARG, 0), + asBCINFO(CMPf, rW_rW_ARG, 0), + asBCINFO(CMPi, rW_rW_ARG, 0), + asBCINFO(CMPIi, rW_DW_ARG, 0), + asBCINFO(CMPIf, rW_DW_ARG, 0), + asBCINFO(CMPIu, rW_DW_ARG, 0), + asBCINFO(JMPP, rW_ARG, 0), + asBCINFO(PopRPtr, NO_ARG, -AS_PTR_SIZE), + asBCINFO(PshRPtr, NO_ARG, AS_PTR_SIZE), + asBCINFO(STR, W_ARG, 1+AS_PTR_SIZE), + asBCINFO(CALLSYS, DW_ARG, 0xFFFF), + asBCINFO(CALLBND, DW_ARG, 0xFFFF), + asBCINFO(SUSPEND, NO_ARG, 0), + asBCINFO(ALLOC, PTR_DW_ARG, 0xFFFF), + asBCINFO(FREE, wW_PTR_ARG, 0), + asBCINFO(LOADOBJ, rW_ARG, 0), + asBCINFO(STOREOBJ, wW_ARG, 0), + asBCINFO(GETOBJ, W_ARG, 0), + asBCINFO(REFCPY, PTR_ARG, -AS_PTR_SIZE), + asBCINFO(CHKREF, NO_ARG, 0), + asBCINFO(GETOBJREF, W_ARG, 0), + asBCINFO(GETREF, W_ARG, 0), + asBCINFO(PshNull, NO_ARG, AS_PTR_SIZE), + asBCINFO(ClrVPtr, wW_ARG, 0), + asBCINFO(OBJTYPE, PTR_ARG, AS_PTR_SIZE), + asBCINFO(TYPEID, DW_ARG, 1), + asBCINFO(SetV4, wW_DW_ARG, 0), + asBCINFO(SetV8, wW_QW_ARG, 0), + asBCINFO(ADDSi, W_DW_ARG, 0), + asBCINFO(CpyVtoV4, wW_rW_ARG, 0), + asBCINFO(CpyVtoV8, wW_rW_ARG, 0), + asBCINFO(CpyVtoR4, rW_ARG, 0), + asBCINFO(CpyVtoR8, rW_ARG, 0), + asBCINFO(CpyVtoG4, rW_PTR_ARG, 0), + asBCINFO(CpyRtoV4, wW_ARG, 0), + asBCINFO(CpyRtoV8, wW_ARG, 0), + asBCINFO(CpyGtoV4, wW_PTR_ARG, 0), + asBCINFO(WRTV1, rW_ARG, 0), + asBCINFO(WRTV2, rW_ARG, 0), + asBCINFO(WRTV4, rW_ARG, 0), + asBCINFO(WRTV8, rW_ARG, 0), + asBCINFO(RDR1, wW_ARG, 0), + asBCINFO(RDR2, wW_ARG, 0), + asBCINFO(RDR4, wW_ARG, 0), + asBCINFO(RDR8, wW_ARG, 0), + asBCINFO(LDG, PTR_ARG, 0), + asBCINFO(LDV, rW_ARG, 0), + asBCINFO(PGA, PTR_ARG, AS_PTR_SIZE), + asBCINFO(CmpPtr, rW_rW_ARG, 0), + asBCINFO(VAR, rW_ARG, AS_PTR_SIZE), + asBCINFO(iTOf, rW_ARG, 0), + asBCINFO(fTOi, rW_ARG, 0), + asBCINFO(uTOf, rW_ARG, 0), + asBCINFO(fTOu, rW_ARG, 0), + asBCINFO(sbTOi, rW_ARG, 0), + asBCINFO(swTOi, rW_ARG, 0), + asBCINFO(ubTOi, rW_ARG, 0), + asBCINFO(uwTOi, rW_ARG, 0), + asBCINFO(dTOi, wW_rW_ARG, 0), + asBCINFO(dTOu, wW_rW_ARG, 0), + asBCINFO(dTOf, wW_rW_ARG, 0), + asBCINFO(iTOd, wW_rW_ARG, 0), + asBCINFO(uTOd, wW_rW_ARG, 0), + asBCINFO(fTOd, wW_rW_ARG, 0), + asBCINFO(ADDi, wW_rW_rW_ARG, 0), + asBCINFO(SUBi, wW_rW_rW_ARG, 0), + asBCINFO(MULi, wW_rW_rW_ARG, 0), + asBCINFO(DIVi, wW_rW_rW_ARG, 0), + asBCINFO(MODi, wW_rW_rW_ARG, 0), + asBCINFO(ADDf, wW_rW_rW_ARG, 0), + asBCINFO(SUBf, wW_rW_rW_ARG, 0), + asBCINFO(MULf, wW_rW_rW_ARG, 0), + asBCINFO(DIVf, wW_rW_rW_ARG, 0), + asBCINFO(MODf, wW_rW_rW_ARG, 0), + asBCINFO(ADDd, wW_rW_rW_ARG, 0), + asBCINFO(SUBd, wW_rW_rW_ARG, 0), + asBCINFO(MULd, wW_rW_rW_ARG, 0), + asBCINFO(DIVd, wW_rW_rW_ARG, 0), + asBCINFO(MODd, wW_rW_rW_ARG, 0), + asBCINFO(ADDIi, wW_rW_DW_ARG, 0), + asBCINFO(SUBIi, wW_rW_DW_ARG, 0), + asBCINFO(MULIi, wW_rW_DW_ARG, 0), + asBCINFO(ADDIf, wW_rW_DW_ARG, 0), + asBCINFO(SUBIf, wW_rW_DW_ARG, 0), + asBCINFO(MULIf, wW_rW_DW_ARG, 0), + asBCINFO(SetG4, PTR_DW_ARG, 0), + asBCINFO(ChkRefS, NO_ARG, 0), + asBCINFO(ChkNullV, rW_ARG, 0), + asBCINFO(CALLINTF, DW_ARG, 0xFFFF), + asBCINFO(iTOb, rW_ARG, 0), + asBCINFO(iTOw, rW_ARG, 0), + asBCINFO(SetV1, wW_DW_ARG, 0), + asBCINFO(SetV2, wW_DW_ARG, 0), + asBCINFO(Cast, DW_ARG, -AS_PTR_SIZE), + asBCINFO(i64TOi, wW_rW_ARG, 0), + asBCINFO(uTOi64, wW_rW_ARG, 0), + asBCINFO(iTOi64, wW_rW_ARG, 0), + asBCINFO(fTOi64, wW_rW_ARG, 0), + asBCINFO(dTOi64, rW_ARG, 0), + asBCINFO(fTOu64, wW_rW_ARG, 0), + asBCINFO(dTOu64, rW_ARG, 0), + asBCINFO(i64TOf, wW_rW_ARG, 0), + asBCINFO(u64TOf, wW_rW_ARG, 0), + asBCINFO(i64TOd, rW_ARG, 0), + asBCINFO(u64TOd, rW_ARG, 0), + asBCINFO(NEGi64, rW_ARG, 0), + asBCINFO(INCi64, NO_ARG, 0), + asBCINFO(DECi64, NO_ARG, 0), + asBCINFO(BNOT64, rW_ARG, 0), + asBCINFO(ADDi64, wW_rW_rW_ARG, 0), + asBCINFO(SUBi64, wW_rW_rW_ARG, 0), + asBCINFO(MULi64, wW_rW_rW_ARG, 0), + asBCINFO(DIVi64, wW_rW_rW_ARG, 0), + asBCINFO(MODi64, wW_rW_rW_ARG, 0), + asBCINFO(BAND64, wW_rW_rW_ARG, 0), + asBCINFO(BOR64, wW_rW_rW_ARG, 0), + asBCINFO(BXOR64, wW_rW_rW_ARG, 0), + asBCINFO(BSLL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRA64, wW_rW_rW_ARG, 0), + asBCINFO(CMPi64, rW_rW_ARG, 0), + asBCINFO(CMPu64, rW_rW_ARG, 0), + asBCINFO(ChkNullS, W_ARG, 0), + asBCINFO(ClrHi, NO_ARG, 0), + asBCINFO(JitEntry, PTR_ARG, 0), + asBCINFO(CallPtr, rW_ARG, 0xFFFF), + asBCINFO(FuncPtr, PTR_ARG, AS_PTR_SIZE), + asBCINFO(LoadThisR, W_DW_ARG, 0), + asBCINFO(PshV8, rW_ARG, 2), + asBCINFO(DIVu, wW_rW_rW_ARG, 0), + asBCINFO(MODu, wW_rW_rW_ARG, 0), + asBCINFO(DIVu64, wW_rW_rW_ARG, 0), + asBCINFO(MODu64, wW_rW_rW_ARG, 0), + asBCINFO(LoadRObjR, rW_W_DW_ARG, 0), + asBCINFO(LoadVObjR, rW_W_DW_ARG, 0), + asBCINFO(RefCpyV, wW_PTR_ARG, 0), + asBCINFO(JLowZ, DW_ARG, 0), + asBCINFO(JLowNZ, DW_ARG, 0), + asBCINFO(AllocMem, wW_DW_ARG, 0), + asBCINFO(SetListSize, rW_DW_DW_ARG, 0), + asBCINFO(PshListElmnt, rW_DW_ARG, AS_PTR_SIZE), + asBCINFO(SetListType, rW_DW_DW_ARG, 0), + asBCINFO(POWi, wW_rW_rW_ARG, 0), + asBCINFO(POWu, wW_rW_rW_ARG, 0), + asBCINFO(POWf, wW_rW_rW_ARG, 0), + asBCINFO(POWd, wW_rW_rW_ARG, 0), + asBCINFO(POWdi, wW_rW_rW_ARG, 0), + asBCINFO(POWi64, wW_rW_rW_ARG, 0), + asBCINFO(POWu64, wW_rW_rW_ARG, 0), + asBCINFO(Thiscall1, DW_ARG, -AS_PTR_SIZE-1), + + asBCINFO_DUMMY(201), + asBCINFO_DUMMY(202), + asBCINFO_DUMMY(203), + asBCINFO_DUMMY(204), + asBCINFO_DUMMY(205), + asBCINFO_DUMMY(206), + asBCINFO_DUMMY(207), + asBCINFO_DUMMY(208), + asBCINFO_DUMMY(209), + asBCINFO_DUMMY(210), + asBCINFO_DUMMY(211), + asBCINFO_DUMMY(212), + asBCINFO_DUMMY(213), + asBCINFO_DUMMY(214), + asBCINFO_DUMMY(215), + asBCINFO_DUMMY(216), + asBCINFO_DUMMY(217), + asBCINFO_DUMMY(218), + asBCINFO_DUMMY(219), + asBCINFO_DUMMY(220), + asBCINFO_DUMMY(221), + asBCINFO_DUMMY(222), + asBCINFO_DUMMY(223), + asBCINFO_DUMMY(224), + asBCINFO_DUMMY(225), + asBCINFO_DUMMY(226), + asBCINFO_DUMMY(227), + asBCINFO_DUMMY(228), + asBCINFO_DUMMY(229), + asBCINFO_DUMMY(230), + asBCINFO_DUMMY(231), + asBCINFO_DUMMY(232), + asBCINFO_DUMMY(233), + asBCINFO_DUMMY(234), + asBCINFO_DUMMY(235), + asBCINFO_DUMMY(236), + asBCINFO_DUMMY(237), + asBCINFO_DUMMY(238), + asBCINFO_DUMMY(239), + asBCINFO_DUMMY(240), + asBCINFO_DUMMY(241), + asBCINFO_DUMMY(242), + asBCINFO_DUMMY(243), + asBCINFO_DUMMY(244), + asBCINFO_DUMMY(245), + asBCINFO_DUMMY(246), + asBCINFO_DUMMY(247), + asBCINFO_DUMMY(248), + asBCINFO_DUMMY(249), + asBCINFO_DUMMY(250), + + asBCINFO(VarDecl, W_ARG, 0), + asBCINFO(Block, INFO, 0), + asBCINFO(ObjInfo, rW_DW_ARG, 0), + asBCINFO(LINE, INFO, 0), + asBCINFO(LABEL, INFO, 0) +}; + +// Macros to access bytecode instruction arguments +#define asBC_DWORDARG(x) (*(((asDWORD*)x)+1)) +#define asBC_INTARG(x) (*(int*)(((asDWORD*)x)+1)) +#define asBC_QWORDARG(x) (*(asQWORD*)(((asDWORD*)x)+1)) +#define asBC_FLOATARG(x) (*(float*)(((asDWORD*)x)+1)) +#define asBC_PTRARG(x) (*(asPWORD*)(((asDWORD*)x)+1)) +#define asBC_WORDARG0(x) (*(((asWORD*)x)+1)) +#define asBC_WORDARG1(x) (*(((asWORD*)x)+2)) +#define asBC_SWORDARG0(x) (*(((short*)x)+1)) +#define asBC_SWORDARG1(x) (*(((short*)x)+2)) +#define asBC_SWORDARG2(x) (*(((short*)x)+3)) + + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_array.h b/3rdparty/angelscript/include/as_array.h new file mode 100644 index 0000000..6474951 --- /dev/null +++ b/3rdparty/angelscript/include/as_array.h @@ -0,0 +1,528 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +#ifndef AS_ARRAY_H +#define AS_ARRAY_H + +#if !defined(AS_NO_MEMORY_H) +#include +#endif +#include // some compilers declare memcpy() here + +#ifdef _MSC_VER +#pragma warning(disable:4345) // warning about a change in how the code is handled in this version +#endif + +BEGIN_AS_NAMESPACE + +template class asCArray +{ +public: + asCArray(); + asCArray(const asCArray &); + asCArray(asUINT reserve); + ~asCArray(); + + void Allocate(asUINT numElements, bool keepData); + void AllocateNoConstruct(asUINT numElements, bool keepData); + asUINT GetCapacity() const; + + void PushLast(const T &element); + T PopLast(); + + bool SetLength(asUINT numElements); + bool SetLengthNoConstruct(asUINT numElements); + asUINT GetLength() const; + + void Copy(const T*, asUINT count); + asCArray &operator =(const asCArray &); + void SwapWith(asCArray &other); + + const T &operator [](asUINT index) const; + T &operator [](asUINT index); + T *AddressOf(); + const T *AddressOf() const; + + bool Concatenate(const asCArray &); + void Concatenate(T*, unsigned int count); + + bool Exists(const T &element) const; + int IndexOf(const T &element) const; + void RemoveIndex(asUINT index); // Removes the entry without reordering the array + void RemoveValue(const T &element); // Removes the value without reordering the array + void RemoveIndexUnordered(asUINT index); // Removes the entry without keeping the order + + bool operator==(const asCArray &) const; + bool operator!=(const asCArray &) const; + +protected: + T *array; + asUINT length; // 32bits is enough for all uses of this array + asUINT maxLength; + char buf[2*4*AS_PTR_SIZE]; // Avoid dynamically allocated memory for tiny arrays +}; + +// Implementation + +template +T *asCArray::AddressOf() +{ + return array; +} + +template +const T *asCArray::AddressOf() const +{ + return array; +} + +template +asCArray::asCArray(void) +{ + array = 0; + length = 0; + maxLength = 0; +} + +template +asCArray::asCArray(const asCArray ©) +{ + array = 0; + length = 0; + maxLength = 0; + + *this = copy; +} + +template +asCArray::asCArray(asUINT reserve) +{ + array = 0; + length = 0; + maxLength = 0; + + Allocate(reserve, false); +} + +template +asCArray::~asCArray(void) +{ + // Allocating a zero length array will free all memory + Allocate(0,0); +} + +template +asUINT asCArray::GetLength() const +{ + return length; +} + +template +const T &asCArray::operator [](asUINT index) const +{ + asASSERT(index < length); + + return array[index]; +} + +template +T &asCArray::operator [](asUINT index) +{ + asASSERT(index < length); + + return array[index]; +} + +template +void asCArray::PushLast(const T &element) +{ + if( length == maxLength ) + { + if( maxLength == 0 ) + Allocate(1, false); + else + Allocate(2*maxLength, true); + + if( length == maxLength ) + { + // Out of memory. Return without doing anything + return; + } + } + + array[length++] = element; +} + +template +T asCArray::PopLast() +{ + asASSERT(length > 0); + + return array[--length]; +} + +template +void asCArray::Allocate(asUINT numElements, bool keepData) +{ + // We have 4 situations + // 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller + // 2. The previous array is 8 bytes or smaller and the new array is larger than 8 bytes + // 3. The previous array is larger than 8 bytes and the new array is 8 bytes or smaller + // 4. The previous array is larger than 8 bytes and the new array is also larger than 8 bytes + + T *tmp = 0; + if( numElements ) + { + if( sizeof(T)*numElements <= sizeof(buf) ) + // Use the internal buffer + tmp = reinterpret_cast(buf); + else + { + // Allocate the array and construct each of the elements + tmp = asNEWARRAY(T,numElements); + if( tmp == 0 ) + { + // Out of memory. Return without doing anything + return; + } + } + + if( array == tmp ) + { + // Construct only the newly allocated elements + for( asUINT n = length; n < numElements; n++ ) + new (&tmp[n]) T(); + } + else + { + // Construct all elements + for( asUINT n = 0; n < numElements; n++ ) + new (&tmp[n]) T(); + } + } + + if( array ) + { + asUINT oldLength = length; + + if( array == tmp ) + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + } + else + length = 0; + + // Call the destructor for elements that are no longer used + for( asUINT n = length; n < oldLength; n++ ) + array[n].~T(); + } + else + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + + for( asUINT n = 0; n < length; n++ ) + tmp[n] = array[n]; + } + else + length = 0; + + // Call the destructor for all elements + for( asUINT n = 0; n < oldLength; n++ ) + array[n].~T(); + + if( array != reinterpret_cast(buf) ) + asDELETEARRAY(array); + } + } + + array = tmp; + maxLength = numElements; +} + +template +void asCArray::AllocateNoConstruct(asUINT numElements, bool keepData) +{ + // We have 4 situations + // 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller + // 2. The previous array is 8 bytes or smaller and the new array is larger than 8 bytes + // 3. The previous array is larger than 8 bytes and the new array is 8 bytes or smaller + // 4. The previous array is larger than 8 bytes and the new array is also larger than 8 bytes + + T *tmp = 0; + if( numElements ) + { + if( sizeof(T)*numElements <= sizeof(buf) ) + // Use the internal buffer + tmp = reinterpret_cast(buf); + else + { + // Allocate the array and construct each of the elements + tmp = asNEWARRAY(T,numElements); + if( tmp == 0 ) + { + // Out of memory. Return without doing anything + return; + } + } + } + + if( array ) + { + if( array == tmp ) + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + } + else + length = 0; + } + else + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + + memcpy(tmp, array, sizeof(T)*length); + } + else + length = 0; + + if( array != reinterpret_cast(buf) ) + asDELETEARRAY(array); + } + } + + array = tmp; + maxLength = numElements; +} + +template +asUINT asCArray::GetCapacity() const +{ + return maxLength; +} + +template +bool asCArray::SetLength(asUINT numElements) +{ + if( numElements > maxLength ) + { + Allocate(numElements, true); + if( numElements > maxLength ) + { + // Out of memory. Return without doing anything + return false; + } + } + + length = numElements; + return true; +} + +template +bool asCArray::SetLengthNoConstruct(asUINT numElements) +{ + if( numElements > maxLength ) + { + AllocateNoConstruct(numElements, true); + if( numElements > maxLength ) + { + // Out of memory. Return without doing anything + return false; + } + } + + length = numElements; + return true; +} + +template +void asCArray::Copy(const T *data, asUINT count) +{ + if( maxLength < count ) + { + Allocate(count, false); + if( maxLength < count ) + { + // Out of memory. Return without doing anything + return; + } + } + + for( asUINT n = 0; n < count; n++ ) + array[n] = data[n]; + + length = count; +} + +template +asCArray &asCArray::operator =(const asCArray ©) +{ + Copy(copy.array, copy.length); + + return *this; +} + +template +void asCArray::SwapWith(asCArray &other) +{ + T *tmpArray = array; + asUINT tmpLength = length; + asUINT tmpMaxLength = maxLength; + char tmpBuf[sizeof(buf)]; + memcpy(tmpBuf, buf, sizeof(buf)); + + array = other.array; + length = other.length; + maxLength = other.maxLength; + memcpy(buf, other.buf, sizeof(buf)); + + other.array = tmpArray; + other.length = tmpLength; + other.maxLength = tmpMaxLength; + memcpy(other.buf, tmpBuf, sizeof(buf)); + + // If the data is in the internal buffer, then the array pointer must refer to it + if( array == reinterpret_cast(other.buf) ) + array = reinterpret_cast(buf); + if( other.array == reinterpret_cast(buf) ) + other.array = reinterpret_cast(other.buf); +} + +template +bool asCArray::operator ==(const asCArray &other) const +{ + if( length != other.length ) return false; + + for( asUINT n = 0; n < length; n++ ) + if( array[n] != other.array[n] ) + return false; + + return true; +} + +template +bool asCArray::operator !=(const asCArray &other) const +{ + return !(*this == other); +} + + +// Returns false if the concatenation wasn't successful due to out of memory +template +bool asCArray::Concatenate(const asCArray &other) +{ + if( maxLength < length + other.length ) + { + Allocate(length + other.length, true); + if( maxLength < length + other.length ) + { + // Out of memory + return false; + } + } + + for( asUINT n = 0; n < other.length; n++ ) + array[length+n] = other.array[n]; + + length += other.length; + + // Success + return true; +} + +template +void asCArray::Concatenate(T* other, unsigned int count) +{ + for( unsigned int c = 0; c < count; c++ ) + PushLast(other[c]); +} + +template +bool asCArray::Exists(const T &e) const +{ + return IndexOf(e) == -1 ? false : true; +} + +template +int asCArray::IndexOf(const T &e) const +{ + for( asUINT n = 0; n < length; n++ ) + if( array[n] == e ) return static_cast(n); + + return -1; +} + +template +void asCArray::RemoveIndex(asUINT index) +{ + if( index < length ) + { + for( asUINT n = index; n < length-1; n++ ) + array[n] = array[n+1]; + + PopLast(); + } +} + +template +void asCArray::RemoveValue(const T &e) +{ + for( asUINT n = 0; n < length; n++ ) + { + if( array[n] == e ) + { + RemoveIndex(n); + break; + } + } +} + +template +void asCArray::RemoveIndexUnordered(asUINT index) +{ + if( index == length - 1 ) + PopLast(); + else if( index < length ) + array[index] = PopLast(); +} + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_atomic.h b/3rdparty/angelscript/include/as_atomic.h new file mode 100644 index 0000000..c6fea88 --- /dev/null +++ b/3rdparty/angelscript/include/as_atomic.h @@ -0,0 +1,69 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2013 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_atomic.h +// +// The asCAtomic class provides methods for performing threadsafe +// operations on a single dword, e.g. reference counting and +// bitfields. +// + + + +#ifndef AS_ATOMIC_H +#define AS_ATOMIC_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +class asCAtomic +{ +public: + asCAtomic(); + + asDWORD get() const; + void set(asDWORD val); + + // Increase and return new value + asDWORD atomicInc(); + + // Decrease and return new value + asDWORD atomicDec(); + +protected: + asDWORD value; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_builder.h b/3rdparty/angelscript/include/as_builder.h new file mode 100644 index 0000000..d2d4e59 --- /dev/null +++ b/3rdparty/angelscript/include/as_builder.h @@ -0,0 +1,259 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_builder.h +// +// This is the class that manages the compilation of the scripts +// + + +#ifndef AS_BUILDER_H +#define AS_BUILDER_H + +#include "as_config.h" +#include "as_symboltable.h" +#include "as_scriptengine.h" +#include "as_module.h" +#include "as_array.h" +#include "as_scriptcode.h" +#include "as_scriptnode.h" +#include "as_datatype.h" +#include "as_property.h" + +BEGIN_AS_NAMESPACE + +#ifdef AS_NO_COMPILER +// Forward declare the structure, as it is part of some function signatures used even without the compiler +struct sGlobalVariableDescription; +#endif + +#ifndef AS_NO_COMPILER + +struct sFunctionDescription +{ + asCScriptCode *script; + asCScriptNode *node; + asCString name; + asCObjectType *objType; + asCArray paramNames; + int funcId; + bool isExistingShared; +}; + +struct sGlobalVariableDescription +{ + asCScriptCode *script; + asCScriptNode *declaredAtNode; + asCScriptNode *initializationNode; + asCString name; + asCGlobalProperty *property; + asCDataType datatype; + asSNameSpace *ns; + int index; + bool isCompiled; + bool isPureConstant; + bool isEnumValue; + asQWORD constantValue; +}; + +struct sPropertyInitializer +{ + sPropertyInitializer() : declNode(0), initNode(0), file(0) {} + sPropertyInitializer(const asCString &nm, asCScriptNode *decl, asCScriptNode *init, asCScriptCode *f) : name(nm), declNode(decl), initNode(init), file(f) {} + sPropertyInitializer &operator=(const sPropertyInitializer &o) {name = o.name; declNode = o.declNode; initNode = o.initNode; file = o.file; return *this;} + + asCString name; + asCScriptNode *declNode; + asCScriptNode *initNode; + asCScriptCode *file; +}; + +struct sClassDeclaration +{ + sClassDeclaration() {script = 0; node = 0; validState = 0; typeInfo = 0; isExistingShared = false; isFinal = false;} + + asCScriptCode *script; + asCScriptNode *node; + asCString name; + int validState; + asCTypeInfo *typeInfo; + bool isExistingShared; + bool isFinal; + + asCArray propInits; +}; + +struct sFuncDef +{ + asCScriptCode *script; + asCScriptNode *node; + asCString name; + int idx; +}; + +struct sMixinClass +{ + asCScriptCode *script; + asCScriptNode *node; + asCString name; + asSNameSpace *ns; +}; + +#endif // AS_NO_COMPILER + +class asCBuilder +{ +public: + asCBuilder(asCScriptEngine *engine, asCModule *module); + ~asCBuilder(); + + // These methods are used by the application interface + int VerifyProperty(asCDataType *dt, const char *decl, asCString &outName, asCDataType &outType, asSNameSpace *ns); + int ParseDataType(const char *datatype, asCDataType *result, asSNameSpace *implicitNamespace, bool isReturnType = false); + int ParseTemplateDecl(const char *decl, asCString *name, asCArray &subtypeNames); + int ParseFunctionDeclaration(asCObjectType *type, const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray *paramAutoHandles = 0, bool *returnAutoHandle = 0, asSNameSpace *ns = 0, asCScriptNode **outListPattern = 0, asCObjectType **outParentClass = 0); + int ParseVariableDeclaration(const char *decl, asSNameSpace *implicitNamespace, asCString &outName, asSNameSpace *&outNamespace, asCDataType &outDt); + int CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code, asSNameSpace *ns); + int CheckNameConflictMember(asCTypeInfo *type, const char *name, asCScriptNode *node, asCScriptCode *code, bool isProperty); + +#ifndef AS_NO_COMPILER + int AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy); + int Build(); + + int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc); + int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset); +#endif + +protected: + friend class asCModule; + friend class asCParser; + friend class asCScriptFunction; + friend class asCScriptEngine; + + void Reset(); + + void WriteInfo(const asCString &scriptname, const asCString &msg, int r, int c, bool preMessage); + void WriteInfo(const asCString &msg, asCScriptCode *file, asCScriptNode *node); + void WriteError(const asCString &scriptname, const asCString &msg, int r, int c); + void WriteError(const asCString &msg, asCScriptCode *file, asCScriptNode *node); + void WriteWarning(const asCString &scriptname, const asCString &msg, int r, int c); + void WriteWarning(const asCString &msg, asCScriptCode *file, asCScriptNode *node); + + bool DoesGlobalPropertyExist(const char *prop, asSNameSpace *ns, asCGlobalProperty **outProp = 0, sGlobalVariableDescription **outDesc = 0, bool *isAppProp = 0); + asCGlobalProperty *GetGlobalProperty(const char *prop, asSNameSpace *ns, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue, bool *isAppProp); + int ValidateDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func); + asCString GetCleanExpressionString(asCScriptNode *n, asCScriptCode *file); + + asSNameSpace *GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next, asCObjectType **objType = 0); + asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, asCTypeInfo **scopeType = 0, bool isRequired = true); + asCString GetScopeFromNode(asCScriptNode *n, asCScriptCode *script, asCScriptNode **next = 0); + + asCTypeInfo *GetType(const char *type, asSNameSpace *ns, asCObjectType *parentType); + asCObjectType *GetObjectType(const char *type, asSNameSpace *ns); + asCFuncdefType *GetFuncDef(const char *type, asSNameSpace *ns, asCObjectType *parentType); + asCTypeInfo *GetTypeFromTypesKnownByObject(const char *type, asCObjectType *currentType); + asCDataType CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, asSNameSpace *implicitNamespace, bool acceptHandleForScope = false, asCObjectType *currentType = 0); + asCObjectType *GetTemplateInstanceFromNode(asCScriptNode *node, asCScriptCode *file, asCObjectType *templateType, asSNameSpace *implicitNamespace, asCObjectType *currentType, asCScriptNode **next = 0); + asCDataType ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlag, bool *autoHandle); + + int numErrors; + int numWarnings; + bool silent; + + asCScriptEngine *engine; + asCModule *module; + +#ifndef AS_NO_COMPILER +protected: + friend class asCCompiler; + + int CheckForConflictsDueToDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func, asCObjectType *objType); + int GetNamespaceAndNameFromNode(asCScriptNode *n, asCScriptCode *script, asSNameSpace *implicitNs, asSNameSpace *&outNs, asCString &outName); + int RegisterMixinClass(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + sMixinClass *GetMixinClass(const char *name, asSNameSpace *ns); + void IncludePropertiesFromMixins(sClassDeclaration *decl); + void IncludeMethodsFromMixins(sClassDeclaration *decl); + void AddInterfaceToClass(sClassDeclaration *decl, asCScriptNode *errNode, asCObjectType *intf); + void AddInterfaceFromMixinToClass(sClassDeclaration *decl, asCScriptNode *errNode, sMixinClass *mixin); + + int RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCode *file, asCObjectType *object = 0, bool isInterface = false, bool isGlobalFunction = false, asSNameSpace *ns = 0, bool isExistingShared = false, bool isMixin = false); + int RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared); + int RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file, asCObjectType *object = 0, bool isInterface = false, bool isGlobalFunction = false, asSNameSpace *ns = 0, bool isExistingShared = false); + int RegisterImportedFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterInterface(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + int RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns, asCObjectType *parent); + asCScriptFunction *RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns); + void CompleteFuncDef(sFuncDef *funcDef); + void CompileInterfaces(); + void CompileClasses(asUINT originalNumTempl); + void DetermineTypeRelations(); + void GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isProtected, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace); + bool DoesMethodExist(asCObjectType *objType, int methodId, asUINT *methodIndex = 0); + void AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file); + asCObjectProperty *AddPropertyToClass(sClassDeclaration *c, const asCString &name, const asCDataType &type, bool isPrivate, bool isProtected, bool isInherited, asCScriptCode *file = 0, asCScriptNode *node = 0); + int CreateVirtualFunction(asCScriptFunction *func, int idx); + void ParseScripts(); + void RegisterTypesFromScript(asCScriptNode *node, asCScriptCode *script, asSNameSpace *ns); + void RegisterNonTypesFromScript(asCScriptNode *node, asCScriptCode *script, asSNameSpace *ns); + void CompileFunctions(); + void CompileGlobalVariables(); + int GetEnumValueFromType(asCEnumType *type, const char *name, asCDataType &outDt, asDWORD &outValue); + int GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue, asSNameSpace *ns); + bool DoesTypeExist(const asCString &type); + asCObjectProperty *GetObjectProperty(asCDataType &obj, const char *prop); + asCScriptFunction *GetFunctionDescription(int funcId); + void GetFunctionDescriptions(const char *name, asCArray &funcs, asSNameSpace *ns); + void GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope = "", asCScriptNode *errNode = 0, asCScriptCode *script = 0); + void EvaluateTemplateInstances(asUINT startIdx, bool keepSilent); + + asCArray scripts; + asCArray functions; + asCSymbolTable globVariables; + asCArray classDeclarations; + asCArray interfaceDeclarations; + asCArray namedTypeDeclarations; + asCArray funcDefs; + asCArray mixinClasses; + + // For use with the DoesTypeExists() method + bool hasCachedKnownTypes; + asCMap knownTypes; +#endif +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_bytecode.h b/3rdparty/angelscript/include/as_bytecode.h new file mode 100644 index 0000000..1c1067d --- /dev/null +++ b/3rdparty/angelscript/include/as_bytecode.h @@ -0,0 +1,202 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_bytecode.h +// +// A class for constructing the final byte code +// + + + +#ifndef AS_BYTECODE_H +#define AS_BYTECODE_H + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +#define BYTECODE_SIZE 4 +#define MAX_DATA_SIZE 8 +#define MAX_INSTR_SIZE (BYTECODE_SIZE+MAX_DATA_SIZE) + +class asCScriptEngine; +class asCScriptFunction; +class asCByteInstruction; + +class asCByteCode +{ +public: + asCByteCode(asCScriptEngine *engine); + ~asCByteCode(); + + void ClearAll(); + + int GetSize(); + + void Finalize(const asCArray &tempVariableOffsets); + + void Optimize(); + void OptimizeLocally(const asCArray &tempVariableOffsets); + void ExtractLineNumbers(); + void ExtractObjectVariableInfo(asCScriptFunction *outFunc); + int ResolveJumpAddresses(); + int FindLabel(int label, asCByteInstruction *from, asCByteInstruction **dest, int *positionDelta); + + void AddPath(asCArray &paths, asCByteInstruction *instr, int stackSize); + + void Output(asDWORD *array); + void AddCode(asCByteCode *bc); + + void PostProcess(); + +#ifdef AS_DEBUG + void DebugOutput(const char *name, asCScriptFunction *func); +#endif + + int GetLastInstr(); + int RemoveLastInstr(); + asDWORD GetLastInstrValueDW(); + + void InsertIfNotExists(asCArray &vars, int var); + void GetVarsUsed(asCArray &vars); + bool IsVarUsed(int offset); + void ExchangeVar(int oldOffset, int newOffset); + bool IsSimpleExpression(); + + void Label(short label); + void Line(int line, int column, int scriptIdx); + void ObjInfo(int offset, int info); + void Block(bool start); + void VarDecl(int varDeclIdx); + void Call(asEBCInstr bc, int funcID, int pop); + void CallPtr(asEBCInstr bc, int funcPtrVar, int pop); + void Alloc(asEBCInstr bc, void *objID, int funcID, int pop); + void Ret(int pop); + void JmpP(int var, asDWORD max); + + int InsertFirstInstrDWORD(asEBCInstr bc, asDWORD param); + int InsertFirstInstrQWORD(asEBCInstr bc, asQWORD param); + int Instr(asEBCInstr bc); + int InstrQWORD(asEBCInstr bc, asQWORD param); + int InstrDOUBLE(asEBCInstr bc, double param); + int InstrPTR(asEBCInstr bc, void *param); + int InstrDWORD(asEBCInstr bc, asDWORD param); + int InstrWORD(asEBCInstr bc, asWORD param); + int InstrSHORT(asEBCInstr bc, short param); + int InstrFLOAT(asEBCInstr bc, float param); + int InstrINT(asEBCInstr bc, int param); + int InstrW_W_W(asEBCInstr bc, int a, int b, int c); + int InstrSHORT_B(asEBCInstr bc, short a, asBYTE b); + int InstrSHORT_W(asEBCInstr bc, short a, asWORD b); + int InstrSHORT_DW(asEBCInstr bc, short a, asDWORD b); + int InstrSHORT_QW(asEBCInstr bc, short a, asQWORD b); + int InstrW_DW(asEBCInstr bc, asWORD a, asDWORD b); + int InstrW_QW(asEBCInstr bc, asWORD a, asQWORD b); + int InstrW_PTR(asEBCInstr bc, short a, void *param); + int InstrW_FLOAT(asEBCInstr bc, asWORD a, float b); + int InstrW_W(asEBCInstr bc, int w, int b); + int InstrSHORT_DW_DW(asEBCInstr bc, short a, asDWORD b, asDWORD c); + + asCScriptEngine *GetEngine() const { return engine; }; + + asCArray lineNumbers; + asCArray sectionIdxs; + int largestStackUsed; + +protected: + // Assignments are not allowed + void operator=(const asCByteCode &) {} + + // Helpers for Optimize + bool CanBeSwapped(asCByteInstruction *curr); + asCByteInstruction *ChangeFirstDeleteNext(asCByteInstruction *curr, asEBCInstr bc); + asCByteInstruction *DeleteFirstChangeNext(asCByteInstruction *curr, asEBCInstr bc); + asCByteInstruction *DeleteInstruction(asCByteInstruction *instr); + void RemoveInstruction(asCByteInstruction *instr); + asCByteInstruction *GoBack(asCByteInstruction *curr); + asCByteInstruction *GoForward(asCByteInstruction *curr); + void InsertBefore(asCByteInstruction *before, asCByteInstruction *instr); + bool RemoveUnusedValue(asCByteInstruction *curr, asCByteInstruction **next); + bool IsTemporary(int offset); + bool IsTempRegUsed(asCByteInstruction *curr); + bool IsTempVarRead(asCByteInstruction *curr, int offset); + bool PostponeInitOfTemp(asCByteInstruction *curr, asCByteInstruction **next); + bool IsTempVarReadByInstr(asCByteInstruction *curr, int var); + bool IsTempVarOverwrittenByInstr(asCByteInstruction *curr, int var); + bool IsInstrJmpOrLabel(asCByteInstruction *curr); + + int AddInstruction(); + int AddInstructionFirst(); + + asCByteInstruction *first; + asCByteInstruction *last; + + const asCArray *temporaryVariables; + + asCScriptEngine *engine; +}; + +class asCByteInstruction +{ +public: + asCByteInstruction(); + + void AddAfter(asCByteInstruction *nextCode); + void AddBefore(asCByteInstruction *nextCode); + void Remove(); + + int GetSize(); + int GetStackIncrease(); + + asCByteInstruction *next; + asCByteInstruction *prev; + + asEBCInstr op; + asQWORD arg; + short wArg[3]; + int size; + int stackInc; + + // Testing + bool marked; + int stackSize; +}; + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + +#endif diff --git a/3rdparty/angelscript/include/as_callfunc.h b/3rdparty/angelscript/include/as_callfunc.h new file mode 100644 index 0000000..e65eac8 --- /dev/null +++ b/3rdparty/angelscript/include/as_callfunc.h @@ -0,0 +1,150 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc.h +// +// These functions handle the actual calling of system functions +// + + +#ifndef AS_CALLFUNC_H +#define AS_CALLFUNC_H + +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +class asCContext; +class asCScriptEngine; +class asCScriptFunction; +class asCObjectType; +struct asSSystemFunctionInterface; + +int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *auxiliary, asSSystemFunctionInterface *internal); + +int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + +int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + +int CallSystemFunction(int id, asCContext *context); + +inline asPWORD FuncPtrToUInt(asFUNCTION_t func) +{ + // A little trickery as the C++ standard doesn't allow direct + // conversion between function pointer and data pointer + union { asFUNCTION_t func; asPWORD idx; } u; + u.func = func; + + return u.idx; +} + +enum internalCallConv +{ + ICC_GENERIC_FUNC, + ICC_GENERIC_FUNC_RETURNINMEM, // never used + ICC_CDECL, + ICC_CDECL_RETURNINMEM, + ICC_STDCALL, + ICC_STDCALL_RETURNINMEM, + ICC_THISCALL, + ICC_THISCALL_RETURNINMEM, + ICC_VIRTUAL_THISCALL, + ICC_VIRTUAL_THISCALL_RETURNINMEM, + ICC_CDECL_OBJLAST, + ICC_CDECL_OBJLAST_RETURNINMEM, + ICC_CDECL_OBJFIRST, + ICC_CDECL_OBJFIRST_RETURNINMEM, + ICC_GENERIC_METHOD, + ICC_GENERIC_METHOD_RETURNINMEM, // never used + ICC_THISCALL_OBJLAST, + ICC_THISCALL_OBJLAST_RETURNINMEM, + ICC_VIRTUAL_THISCALL_OBJLAST, + ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM, + ICC_THISCALL_OBJFIRST, + ICC_THISCALL_OBJFIRST_RETURNINMEM, + ICC_VIRTUAL_THISCALL_OBJFIRST, + ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM +}; + +struct asSSystemFunctionInterface +{ + asFUNCTION_t func; + int baseOffset; + internalCallConv callConv; + int scriptReturnSize; + bool hostReturnInMemory; + bool hostReturnFloat; + int hostReturnSize; + int paramSize; + bool takesObjByVal; + asCArray paramAutoHandles; // TODO: Should be able to remove this array. Perhaps the flags can be stored together with the inOutFlags in asCScriptFunction? + bool returnAutoHandle; + void *auxiliary; // can be used for functors, e.g. by asCALL_THISCALL_ASGLOBAL or asCALL_THISCALL_OBJFIRST + + struct SClean + { + asCObjectType *ot; // argument type for clean up + short op; // clean up operation: 0 = release, 1 = free, 2 = destruct then free + short off; // argument offset on the stack + }; + asCArray cleanArgs; + + asSSystemFunctionInterface() {} + + asSSystemFunctionInterface(const asSSystemFunctionInterface &in) + { + *this = in; + } + + asSSystemFunctionInterface &operator=(const asSSystemFunctionInterface &in) + { + func = in.func; + baseOffset = in.baseOffset; + callConv = in.callConv; + scriptReturnSize = in.scriptReturnSize; + hostReturnInMemory = in.hostReturnInMemory; + hostReturnFloat = in.hostReturnFloat; + hostReturnSize = in.hostReturnSize; + paramSize = in.paramSize; + takesObjByVal = in.takesObjByVal; + paramAutoHandles = in.paramAutoHandles; + returnAutoHandle = in.returnAutoHandle; + auxiliary = in.auxiliary; + cleanArgs = in.cleanArgs; + return *this; + } +}; + +END_AS_NAMESPACE + +#endif + diff --git a/3rdparty/angelscript/include/as_compiler.h b/3rdparty/angelscript/include/as_compiler.h new file mode 100644 index 0000000..706deaa --- /dev/null +++ b/3rdparty/angelscript/include/as_compiler.h @@ -0,0 +1,401 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_compiler.h +// +// The class that does the actual compilation of the functions +// + + + +#ifndef AS_COMPILER_H +#define AS_COMPILER_H + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_builder.h" +#include "as_scriptfunction.h" +#include "as_variablescope.h" +#include "as_bytecode.h" +#include "as_array.h" +#include "as_datatype.h" + +BEGIN_AS_NAMESPACE + +// This class represents the value of an expression as evaluated by the compiler. +// It holds information such as the type of the value, stack offset for a local +// variable, value of constants, whether the value can be modified (i.e. lvalue), etc. +struct asCExprValue +{ + asCExprValue(); + void Set(const asCDataType &dataType); + + void SetVariable(const asCDataType &dataType, int stackOffset, bool isTemporary); + void SetConstantB(const asCDataType &dataType, asBYTE value); + void SetConstantQW(const asCDataType &dataType, asQWORD value); + void SetConstantDW(const asCDataType &dataType, asDWORD value); + void SetConstantW(const asCDataType &dataType, asWORD value); + void SetConstantF(const asCDataType &dataType, float value); + void SetConstantD(const asCDataType &dataType, double value); + void SetConstantB(asBYTE value); + void SetConstantW(asWORD value); + void SetConstantQW(asQWORD value); + void SetConstantDW(asDWORD value); + void SetConstantF(float value); + void SetConstantD(double value); + asBYTE GetConstantB(); + asWORD GetConstantW(); + asQWORD GetConstantQW(); + asDWORD GetConstantDW(); + float GetConstantF(); + double GetConstantD(); + + void SetConstantData(const asCDataType &dataType, asQWORD value); + asQWORD GetConstantData(); + + void SetNullConstant(); + void SetUndefinedFuncHandle(asCScriptEngine *engine); + void SetVoid(); + void SetDummy(); + + bool IsUndefinedFuncHandle() const; + bool IsNullConstant() const; + bool IsVoid() const; + + asCDataType dataType; + bool isLValue : 1; // Can this value be updated in assignment, or increment operators, etc + bool isTemporary : 1; + bool isConstant : 1; + bool isVariable : 1; + bool isExplicitHandle : 1; + bool isRefToLocal : 1; // The reference may be to a local variable + bool isHandleSafe : 1; // the life-time of the handle is guaranteed for the duration of the access + short dummy : 9; + short stackOffset; + +private: + // These values must not be accessed directly in order to avoid problems with endianess. + // Use the appropriate accessor methods instead + union + { + asQWORD qwordValue; + double doubleValue; + asDWORD dwordValue; + float floatValue; + asWORD wordValue; + asBYTE byteValue; + }; +}; + +struct asCExprContext; + +// This class holds information for arguments that needs to be +// cleaned up after the result of a function has been evaluated. +struct asSDeferredParam +{ + asSDeferredParam() {argNode = 0; origExpr = 0;} + + asCScriptNode *argNode; + asCExprValue argType; + int argInOutFlags; + asCExprContext *origExpr; +}; + +// TODO: refactor: asCExprContext should have indicators to inform where the value is, +// i.e. if the reference to an object is pushed on the stack or not, etc + +// This class holds information about an expression that is being evaluated, e.g. +// the current bytecode, ambiguous symbol names, property accessors, etc. +struct asCExprContext +{ + asCExprContext(asCScriptEngine *engine); + ~asCExprContext(); + void Clear(); + bool IsClassMethod() const; + bool IsGlobalFunc() const; + void SetLambda(asCScriptNode *funcDecl); + bool IsLambda() const; + void SetVoidExpression(); + bool IsVoidExpression() const; + void Merge(asCExprContext *after); + + asCByteCode bc; + asCExprValue type; + int property_get; + int property_set; + bool property_const; // If the object that is being accessed through property accessor is read-only + bool property_handle; // If the property accessor is called on an object stored in a handle + bool property_ref; // If the property accessor is called on a reference + bool isVoidExpression; // Set to true if the expression is an explicit 'void', e.g. used to ignore out parameters in func calls + bool isCleanArg; // Set to true if the expression has only been initialized with default constructor + asCExprContext *property_arg; + asCArray deferredParams; + asCScriptNode *exprNode; + asCExprContext *origExpr; + // TODO: cleanup: use ambiguousName and an enum to say if it is a method, global func, or enum value + asCString methodName; + asCString enumValue; +}; + +struct asSOverloadCandidate +{ + asSOverloadCandidate() : funcId(0), cost(0) {} + asSOverloadCandidate(int _id, asUINT _cost) : funcId(_id), cost(_cost) {} + int funcId; + asUINT cost; +}; + +struct asSNamedArgument +{ + asCString name; + asCExprContext *ctx; + asUINT match; +}; + +enum EImplicitConv +{ + asIC_IMPLICIT_CONV, + asIC_EXPLICIT_REF_CAST, + asIC_EXPLICIT_VAL_CAST +}; + +enum EConvCost +{ + asCC_NO_CONV = 0, + asCC_CONST_CONV = 1, + asCC_ENUM_SAME_SIZE_CONV = 2, + asCC_ENUM_DIFF_SIZE_CONV = 3, + asCC_PRIMITIVE_SIZE_CONV = 4, + asCC_SIGNED_CONV = 5, + asCC_INT_FLOAT_CONV = 6, + asCC_REF_CONV = 7, + asCC_OBJ_TO_PRIMITIVE_CONV = 8, + asCC_TO_OBJECT_CONV = 9, + asCC_VARIABLE_CONV = 10 +}; + +class asCCompiler +{ +public: + asCCompiler(asCScriptEngine *engine); + ~asCCompiler(); + + int CompileFunction(asCBuilder *builder, asCScriptCode *script, asCArray ¶meterNames, asCScriptNode *func, asCScriptFunction *outFunc, sClassDeclaration *classDecl); + int CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, asCScriptFunction *outFunc, sClassDeclaration *classDecl); + int CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc); + int CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *expr, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc); + +protected: + friend class asCBuilder; + + void Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc); + + // Statements + void CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc); + void CompileDeclaration(asCScriptNode *decl, asCByteCode *bc); + void CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc); + void CompileIfStatement(asCScriptNode *node, bool *hasReturn, asCByteCode *bc); + void CompileSwitchStatement(asCScriptNode *node, bool *hasReturn, asCByteCode *bc); + void CompileCase(asCScriptNode *node, asCByteCode *bc); + void CompileForStatement(asCScriptNode *node, asCByteCode *bc); + void CompileWhileStatement(asCScriptNode *node, asCByteCode *bc); + void CompileDoWhileStatement(asCScriptNode *node, asCByteCode *bc); + void CompileBreakStatement(asCScriptNode *node, asCByteCode *bc); + void CompileContinueStatement(asCScriptNode *node, asCByteCode *bc); + void CompileReturnStatement(asCScriptNode *node, asCByteCode *bc); + void CompileExpressionStatement(asCScriptNode *node, asCByteCode *bc); + + // Expressions + int CompileAssignment(asCScriptNode *expr, asCExprContext *out); + int CompileCondition(asCScriptNode *expr, asCExprContext *out); + int CompileExpression(asCScriptNode *expr, asCExprContext *out); + int CompilePostFixExpression(asCArray *postfix, asCExprContext *out); + int CompileExpressionTerm(asCScriptNode *node, asCExprContext *out); + int CompileExpressionPreOp(asCScriptNode *node, asCExprContext *out); + int CompileExpressionPostOp(asCScriptNode *node, asCExprContext *out); + int CompileExpressionValue(asCScriptNode *node, asCExprContext *out); + int CompileFunctionCall(asCScriptNode *node, asCExprContext *out, asCObjectType *objectType, bool objIsConst, const asCString &scope = ""); + int CompileConstructCall(asCScriptNode *node, asCExprContext *out); + int CompileConversion(asCScriptNode *node, asCExprContext *out); + int CompileOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken, bool leftToRight = true); + void CompileOperatorOnHandles(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileMathOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileBitwiseOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileComparisonOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileBooleanOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, asCExprContext *out, eTokenType opToken = ttUnrecognizedToken); + bool CompileOverloadedDualOperator(asCScriptNode *node, asCExprContext *l, asCExprContext *r, bool leftToRight, asCExprContext *out, bool isHandle = false, eTokenType opToken = ttUnrecognizedToken); + int CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asCExprContext *l, asCExprContext *r, bool leftToRight, asCExprContext *out, bool specificReturn = false, const asCDataType &returnType = asCDataType::CreatePrimitive(ttVoid, false)); + + void CompileInitList(asCExprValue *var, asCScriptNode *node, asCByteCode *bc, int isVarGlobOrMem); + int CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode, int &elementsInSubList); + + int CallDefaultConstructor(const asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem = 0, bool derefDest = false); + int CallCopyConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool isGlobalVar = false, bool derefDestination = false); + void CallDestructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc); + int CompileArgumentList(asCScriptNode *node, asCArray &args, asCArray &namedArgs); + int CompileDefaultAndNamedArgs(asCScriptNode *node, asCArray &args, int funcId, asCObjectType *type, asCArray *namedArgs = 0); + asUINT MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCArray *namedArgs = NULL, asCObjectType *objectType = NULL, bool isConstMethod = false, bool silent = false, bool allowObjectConstruct = true, const asCString &scope = ""); + int CompileVariableAccess(const asCString &name, const asCString &scope, asCExprContext *ctx, asCScriptNode *errNode, bool isOptional = false, bool noFunction = false, bool noGlobal = false, asCObjectType *objType = 0); + void CompileMemberInitialization(asCByteCode *bc, bool onlyDefaults); + bool CompileAutoType(asCDataType &autoType, asCExprContext &compiledCtx, asCScriptNode *exprNode, asCScriptNode *errNode); + bool CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem, asCExprContext *preCompiled = 0); + void CompileInitAsCopy(asCDataType &type, int offset, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool derefDestination); + + // Helper functions + void ConvertToPostFix(asCScriptNode *expr, asCArray &postfix); + void ProcessPropertyGetAccessor(asCExprContext *ctx, asCScriptNode *node); + int ProcessPropertySetAccessor(asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node); + int ProcessPropertyGetSetAccessor(asCExprContext *ctx, asCExprContext *lctx, asCExprContext *rctx, eTokenType op, asCScriptNode *errNode); + int FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess = false); + int FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess = false); + void PrepareTemporaryVariable(asCScriptNode *node, asCExprContext *ctx, bool forceOnHeap = false); + void PrepareOperand(asCExprContext *ctx, asCScriptNode *node); + void PrepareForAssignment(asCDataType *lvalue, asCExprContext *rvalue, asCScriptNode *node, bool toTemporary, asCExprContext *lvalueExpr = 0); + int PerformAssignment(asCExprValue *lvalue, asCExprValue *rvalue, asCByteCode *bc, asCScriptNode *node); + bool IsVariableInitialized(asCExprValue *type, asCScriptNode *node); + void Dereference(asCExprContext *ctx, bool generateCode); + bool CompileRefCast(asCExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode = true); + asUINT MatchArgument(asCArray &funcs, asCArray &matches, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct = true); + int MatchArgument(asCScriptFunction *desc, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct = true); + void PerformFunctionCall(int funcId, asCExprContext *out, bool isConstructor = false, asCArray *args = 0, asCObjectType *objTypeForConstruct = 0, bool useVariable = false, int varOffset = 0, int funcPtrVar = 0); + void MoveArgsToStack(int funcId, asCByteCode *bc, asCArray &args, bool addOneToOffset); + void MakeFunctionCall(asCExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable = false, int stackOffset = 0, int funcPtrVar = 0); + void PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray &args); + void AfterFunctionCall(int funcId, asCArray &args, asCExprContext *ctx, bool deferAll); + void ProcessDeferredParams(asCExprContext *ctx); + int PrepareArgument(asCDataType *paramType, asCExprContext *ctx, asCScriptNode *node, bool isFunction = false, int refType = 0, bool isMakingCopy = false); + void PrepareArgument2(asCExprContext *ctx, asCExprContext *arg, asCDataType *paramType, bool isFunction = false, int refType = 0, bool isMakingCopy = false); + bool IsLValue(asCExprValue &type); + int DoAssignment(asCExprContext *out, asCExprContext *lctx, asCExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, eTokenType op, asCScriptNode *opNode); + void MergeExprBytecode(asCExprContext *before, asCExprContext *after); + void MergeExprBytecodeAndType(asCExprContext *before, asCExprContext *after); + void FilterConst(asCArray &funcs, bool removeConst = true); + void ConvertToVariable(asCExprContext *ctx); + void ConvertToVariableNotIn(asCExprContext *ctx, asCExprContext *exclude); + void ConvertToTempVariable(asCExprContext *ctx); + void ConvertToTempVariableNotIn(asCExprContext *ctx, asCExprContext *exclude); + void ConvertToReference(asCExprContext *ctx); + void PushVariableOnStack(asCExprContext *ctx, bool asReference); + void DestroyVariables(asCByteCode *bc); + asSNameSpace *DetermineNameSpace(const asCString &scope); + int SetupParametersAndReturnVariable(asCArray ¶meterNames, asCScriptNode *func); + + void DetermineSingleFunc(asCExprContext *ctx, asCScriptNode *node); + + // Returns the cost of the conversion (the sum of the EConvCost performed) + asUINT ImplicitConversion(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, bool allowObjectConstruct = true); + asUINT ImplicitConvPrimitiveToPrimitive(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true); + asUINT ImplicitConvObjectToPrimitive(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true); + asUINT ImplicitConvPrimitiveToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, bool allowObjectConstruct = true); + asUINT ImplicitConvObjectToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, bool allowObjectConstruct = true); + asUINT ImplicitConvObjectRef(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode); + asUINT ImplicitConvObjectValue(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode); + void ImplicitConversionConstant(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType); + void ImplicitConvObjectToBestMathType(asCExprContext *ctx, asCScriptNode *node); + asUINT ImplicitConvLambdaToFunc(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true); + + void LineInstr(asCByteCode *bc, size_t pos); + + asUINT ProcessStringConstant(asCString &str, asCScriptNode *node, bool processEscapeSequences = true); + void ProcessHeredocStringConstant(asCString &str, asCScriptNode *node); + int GetPrecedence(asCScriptNode *op); + void Error(const asCString &msg, asCScriptNode *node); + void Warning(const asCString &msg, asCScriptNode *node); + void Information(const asCString &msg, asCScriptNode *node); + void PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node, asCObjectType *inType = 0); + void AddVariableScope(bool isBreakScope = false, bool isContinueScope = false); + void RemoveVariableScope(); + void FinalizeFunction(); + + asCByteCode byteCode; + + bool hasCompileErrors; + + int nextLabel; + int numLambdas; + + asCVariableScope *variables; + asCBuilder *builder; + asCScriptEngine *engine; + asCScriptCode *script; + asCScriptFunction *outFunc; + + bool m_isConstructor; + bool m_isConstructorCalled; + sClassDeclaration *m_classDecl; + sGlobalVariableDescription *m_globalVar; + + asCArray breakLabels; + asCArray continueLabels; + + int AllocateVariable(const asCDataType &type, bool isTemporary, bool forceOnHeap = false); + int AllocateVariableNotIn(const asCDataType &type, bool isTemporary, bool forceOnHeap, asCExprContext *ctx); + int GetVariableOffset(int varIndex); + int GetVariableSlot(int varOffset); + void DeallocateVariable(int pos); + void ReleaseTemporaryVariable(asCExprValue &t, asCByteCode *bc); + void ReleaseTemporaryVariable(int offset, asCByteCode *bc); + bool IsVariableOnHeap(int offset); + + // This ordered array indicates the type of each variable + asCArray variableAllocations; + + // This ordered array indicates which variables are temporaries or not + asCArray variableIsTemporary; + + // This unordered array gives the offsets of all temporary variables, whether currently allocated or not + asCArray tempVariableOffsets; + + // This ordered array indicated if the variable is on the heap or not + asCArray variableIsOnHeap; + + // This unordered array gives the indexes of the currently unused variables + asCArray freeVariables; + + // This array holds the offsets of the currently allocated temporary variables + asCArray tempVariables; + + // This array holds the indices of variables that must not be used in an allocation + asCArray reservedVariables; + + bool isCompilingDefaultArg; + bool isProcessingDeferredParams; + int noCodeOutput; +}; + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + +#endif diff --git a/3rdparty/angelscript/include/as_config.h b/3rdparty/angelscript/include/as_config.h new file mode 100644 index 0000000..8542c18 --- /dev/null +++ b/3rdparty/angelscript/include/as_config.h @@ -0,0 +1,1262 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_config.h +// +// this file is used for configuring the compilation of the library +// + +#ifndef AS_CONFIG_H +#define AS_CONFIG_H + + + +// +// Features +//----------------------------------------- + +// AS_NO_THREADS +// Turns off support for multithreading. By turning off +// this when it's not needed a bit of performance is gained. + +// AS_WINDOWS_THREADS +// If the library should be compiled using windows threads. + +// AS_POSIX_THREADS +// If the library should be compiled using posix threads. + +// AS_NO_ATOMIC +// If the compiler/platform doesn't support atomic instructions +// then this should be defined to use critical sections instead. + +// AS_DEBUG +// This flag can be defined to make the library write some extra output when +// compiling and executing scripts. + +// AS_DEPRECATED +// If this flag is defined then some backwards compatibility is maintained. +// There is no guarantee for how well deprecated functionality will work though +// so it is best to exchange it for the new functionality as soon as possible. + +// AS_NO_CLASS_METHODS +// Disables the possibility to add class methods. Can increase the +// portability of the library. + +// AS_MAX_PORTABILITY +// Disables all platform specific code. Only the asCALL_GENERIC calling +// convention will be available in with this flag set. + +// AS_DOUBLEBYTE_CHARSET +// When this flag is defined, the parser will treat all characters in strings +// that are greater than 127 as lead characters and automatically include the +// next character in the script without checking its value. This should be +// compatible with common encoding schemes, e.g. Big5. Shift-JIS is not compatible +// though as it encodes some single byte characters above 127. +// +// If support for international text is desired, it is recommended that UTF-8 +// is used as this is supported natively by the compiler without the use for this +// preprocessor flag. + +// AS_NO_COMPILER +// Compiles the library without support for compiling scripts. This is intended +// for those applications that will load pre-compiled bytecode and wants to decrease +// the size of the executable. + +// AS_NO_EXCEPTIONS +// Define this if exception handling is turned off or not available on the target platform. + +// AS_NO_MEMBER_INIT +// Disable the support for initialization of class members directly in the declaration. +// This was as a form to maintain backwards compatibility with versions before 2.26.0 +// if the new order of the member initialization caused null pointer exceptions in older +// scripts (e.g. if a base class accessed members of a derived class through a virtual method). + +// AS_USE_NAMESPACE +// Adds the AngelScript namespace on the declarations. + + + +// +// Library usage +//------------------------------------------ + +// ANGELSCRIPT_EXPORT +// This flag should be defined when compiling the library as a lib or dll. + +// ANGELSCRIPT_DLL_LIBRARY_IMPORT +// This flag should be defined when using AngelScript as a dll with automatic +// library import. + +// ANGELSCRIPT_DLL_MANUAL_IMPORT +// This flag should be defined when using AngelScript as a dll with manual +// loading of the library. + + + + +// +// Compiler differences +//----------------------------------------- + +// asVSNPRINTF(a,b,c,d) +// Some compilers use different names for this function. You must +// define this macro to map to the proper function. + +// ASM_AT_N_T or ASM_INTEL +// You should choose what inline assembly syntax to use when compiling. + +// VALUE_OF_BOOLEAN_TRUE +// This flag allows to customize the exact value of boolean true. + +// AS_SIZEOF_BOOL +// On some target platforms the sizeof(bool) is 4, but on most it is 1. + +// STDCALL +// This is used to declare a function to use the stdcall calling convention. + +// AS_NO_MEMORY_H +// Some compilers don't come with the memory.h header file. + +// AS_NO_THISCALL_FUNCTOR_METHOD +// Defined if the support for functor methods hasn't been implemented on the platform. + + + +// +// How to identify different compilers +//----------------------------------------- +// Ref: http://nadeausoftware.com/articles/2012/10/c_c_tip_how_detect_compiler_name_and_version_using_compiler_predefined_macros + +// MS Visual C++ +// _MSC_VER is defined +// __MWERKS__ is not defined + +// Metrowerks +// _MSC_VER is defined +// __MWERKS__ is defined + +// GNU C based compilers +// __GNUC__ is defined + +// CLang/LLVM +// __clang__ is defined + +// Embarcadero C++Builder +// __BORLANDC__ is defined + +// Oracle Solaris Studio (previously known as Sun CC compiler) +// __SUNPRO_CC is defined + + + +// +// CPU differences +//--------------------------------------- + +// AS_USE_DOUBLE_AS_FLOAT +// If there is no 64 bit floating point type, then this constant can be defined +// to treat double like normal floats. + +// AS_X86 +// Use assembler code for the x86 CPU family + +// AS_SH4 +// Use assembler code for the SH4 CPU family + +// AS_MIPS +// Use assembler code for the MIPS CPU family + +// AS_PPC +// Use assembler code for the 32bit PowerPC CPU family + +// AS_PPC_64 +// Use assembler code for the 64bit PowerPC CPU family + +// AS_XENON +// Use assembler code for the Xenon (XBOX360) CPU family + +// AS_ARM +// Use assembler code for the ARM CPU family + +// AS_SOFTFP +// Use to tell compiler that ARM soft-float ABI +// should be used instead of ARM hard-float ABI + +// AS_X64_GCC +// Use GCC assembler code for the X64 AMD/Intel CPU family + +// AS_X64_MSVC +// Use MSVC assembler code for the X64 AMD/Intel CPU family + +// AS_64BIT_PTR +// Define this to make the engine store all pointers in 64bit words. + +// AS_BIG_ENDIAN +// Define this for CPUs that use big endian memory layout, e.g. PPC + +// AS_SPARC +// Define this for SPARC CPU family + + + +// +// Target systems +//-------------------------------- +// This group shows a few of the flags used to identify different target systems. +// Sometimes there are differences on different target systems, while both CPU and +// compiler is the same for both, when this is so these flags are used to produce the +// right code. + +// AS_WIN - Microsoft Windows +// AS_LINUX - Linux +// AS_MAC - Apple Macintosh +// AS_BSD - BSD based OS (FreeBSD, DragonFly, OpenBSD, etc) +// AS_XBOX - Microsoft XBox +// AS_XBOX360 - Microsoft XBox 360 +// AS_PSP - Sony Playstation Portable +// AS_PSVITA - Sony Playstation Vita +// AS_PS2 - Sony Playstation 2 +// AS_PS3 - Sony Playstation 3 +// AS_DC - Sega Dreamcast +// AS_GC - Nintendo GameCube +// AS_WII - Nintendo Wii +// AS_WIIU - Nintendo Wii U +// AS_IPHONE - Apple IPhone +// AS_ANDROID - Android +// AS_HAIKU - Haiku +// AS_ILLUMOS - Illumos like (OpenSolaris, OpenIndiana, NCP, etc) +// AS_MARMALADE - Marmalade cross platform SDK (a layer on top of the OS) +// AS_SUN - Sun UNIX + + + + +// +// Calling conventions +//----------------------------------------- + +// GNU_STYLE_VIRTUAL_METHOD +// This constant should be defined if method pointers store index for virtual +// functions in the same location as the function pointer. In such cases the method +// is identified as virtual if the least significant bit is set. + +// MULTI_BASE_OFFSET(x) +// This macro is used to retrieve the offset added to the object pointer in order to +// implicitly cast the object to the base object. x is the method pointer received by +// the register function. + +// HAVE_VIRTUAL_BASE_OFFSET +// Define this constant if the compiler stores the virtual base offset in the method +// pointers. If it is not stored in the pointers then AngelScript have no way of +// identifying a method as coming from a class with virtual inheritance. + +// VIRTUAL_BASE_OFFSET(x) +// This macro is used to retrieve the offset added to the object pointer in order to +// find the virtual base object. x is the method pointer received by the register +// function; + +// COMPLEX_RETURN_MASK +// This constant shows what attributes determine if an object is returned in memory +// or in the registers as normal structures + +// COMPLEX_MASK +// This constant shows what attributes determine if an object is implicitly passed +// by reference or not, even if the argument is declared by value + +// THISCALL_RETURN_SIMPLE_IN_MEMORY +// CDECL_RETURN_SIMPLE_IN_MEMORY +// STDCALL_RETURN_SIMPLE_IN_MEMORY +// When these constants are defined then the corresponding calling convention always +// return classes/structs in memory regardless of size or complexity. + +// THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// Specifies the minimum size in dwords a class/struct needs to be to be passed in memory + +// CALLEE_POPS_HIDDEN_RETURN_POINTER +// This constant should be defined if the callee pops the hidden return pointer, +// used when returning an object in memory. + +// THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER +// This constant should be defined if the callee pops the hidden return pointer +// for thiscall functions; used when returning an object in memory. + +// THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK +// With this constant defined AngelScript will pass the object pointer on the stack + +// THISCALL_CALLEE_POPS_ARGUMENTS +// If the callee pops arguments for class methods then define this constant + +// COMPLEX_OBJS_PASSED_BY_REF +// Some compilers always pass certain objects by reference. GNUC for example does +// this if the the class has a defined destructor. + +// AS_LARGE_OBJS_PASSED_BY_REF +// If this is defined large objects are passed by reference, whether they are complex or not + +// AS_LARGE_OBJ_MIN_SIZE +// This is the size of objects determined as large ones + +// AS_CALLEE_DESTROY_OBJ_BY_VAL +// When an object is passed by value the called function is the one responsible +// for calling the destructor before returning. + +// HAS_128_BIT_PRIMITIVES +// 64bit processors often support 128bit primitives. These may require special +// treatment when passed in function arguments or returned by functions. + +// SPLIT_OBJS_BY_MEMBER_TYPES +// On some platforms objects with primitive members are split over different +// register types when passed by value to functions. + + + + + +// +// Detect compiler +//------------------------------------------------ + + +#define VALUE_OF_BOOLEAN_TRUE 1 +#define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 +#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 +#define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 +#define THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER + +// Not implemented by default. Undefined with tested platforms. +#define AS_NO_THISCALL_FUNCTOR_METHOD + + +// Embarcadero C++Builder +#if defined(__BORLANDC__) + #ifndef _Windows + #error "Configuration doesn't yet support BCC for Linux or Mac OS." + #endif + #if defined(_M_X64) + #error "Configuration doesn't yet support BCC for AMD64." + #endif + + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define HAVE_VIRTUAL_BASE_OFFSET + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+2)) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define COMPLEX_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR) + #define STDCALL __stdcall + #define AS_SIZEOF_BOOL 1 + #define AS_WINDOWS_THREADS + #undef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER + + #define AS_WIN + #define AS_X86 + #define ASM_INTEL + + #define asVSNPRINTF(a, b, c, d) _vsnprintf(a, b, c, d) + + #define fmodf(a,b) fmod(a,b) + + #define UNREACHABLE_RETURN +#endif + +// Microsoft Visual C++ +// Ref: http://msdn.microsoft.com/en-us/library/b0084kay.aspx +#if defined(_MSC_VER) && !defined(__MWERKS__) + + #if _MSC_VER <= 1200 // MSVC6 + // Disable the useless warnings about truncated symbol names for template instances + #pragma warning( disable : 4786 ) + #endif + + #ifdef _M_X64 + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+2)) + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+4)) + #else + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+3)) + #endif + #define HAVE_VIRTUAL_BASE_OFFSET + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_PASS_OBJECT_POINTER_IN_ECX + + // http://www.madewithmarmalade.com/ + #if defined(__S3E__) + #ifndef AS_MARMALADE + // From now on we'll use the below define + #define AS_MARMALADE + #endif + + // Marmalade doesn't use the Windows libraries + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + + // Marmalade doesn't seem to have proper support for + // atomic instructions or read/write locks, so we turn off + // multithread support + //#define AS_POSIX_THREADS + #define AS_NO_THREADS + #define AS_NO_ATOMIC + + // Marmalade has it's own way of identifying the CPU target + // Note, when building for ARM, the gnuc compiler will always + // be used so we don't need to check for it here + #if defined(I3D_ARCH_X86) + #define AS_X86 + #endif + #else + #if _MSC_VER < 1500 // MSVC++ 9 (aka MSVC++ .NET 2008) + #define asVSNPRINTF(a, b, c, d) _vsnprintf(a, b, c, d) + #else + #define asVSNPRINTF(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d) + #endif + + #define AS_WINDOWS_THREADS + #endif + + #define THISCALL_CALLEE_POPS_ARGUMENTS + #define STDCALL __stdcall + #define AS_SIZEOF_BOOL 1 + #define COMPLEX_OBJS_PASSED_BY_REF + + #define ASM_INTEL // Intel style for inline assembly on microsoft compilers + + #if defined(WIN32) || defined(_WIN32) || defined(_WIN64) + #define AS_WIN + #endif + + #if _XBOX_VER >= 200 + // 360 uses a Xenon processor (which is a modified 64bit PPC) + #define AS_XBOX360 + #define AS_XENON + #define AS_BIG_ENDIAN + #else + #if defined(_XBOX) || (defined(_M_IX86) && !defined(__LP64__)) + #define AS_X86 + #ifndef _XBOX + // Not tested with xbox (only enabled if is Windows) + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #endif + #elif defined(_M_X64) + #define AS_X64_MSVC + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #define AS_LARGE_OBJS_PASSED_BY_REF + #define AS_LARGE_OBJ_MIN_SIZE 3 + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #define COMPLEX_MASK (asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #endif + #endif + + #if defined(_ARM_) || defined(_M_ARM) + #define AS_ARM + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define COMPLEX_MASK (asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) + + // Windows CE uses softfp calling convention, while Windows RT uses hardfp calling convention + // ref: http://stackoverflow.com/questions/16375355/what-is-the-windows-rt-on-arm-native-code-calling-convention + #if defined(_WIN32_WCE) + #define AS_SOFTFP + #endif + #endif + + #ifndef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_ARRAY) + #endif + + #ifndef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) + #endif + + #define UNREACHABLE_RETURN +#endif + +// Metrowerks CodeWarrior (experimental, let me know if something isn't working) +#if defined(__MWERKS__) && !defined(EPPC) // JWC -- If Wii DO NOT use this even when using Metrowerks Compiler. Even though they are called Freescale... + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define HAVE_VIRTUAL_BASE_OFFSET + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+3)) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_PASS_OBJECT_POINTER_IN_ECX + #define asVSNPRINTF(a, b, c, d) _vsnprintf(a, b, c, d) + #define THISCALL_CALLEE_POPS_ARGUMENTS + #define COMPLEX_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #define AS_SIZEOF_BOOL 1 + #define AS_WINDOWS_THREADS + #define STDCALL __stdcall + + // Support native calling conventions on x86, but not 64bit yet + #if defined(_M_IX86) && !defined(__LP64__) + #define AS_X86 + #define ASM_INTEL // Intel style for inline assembly + #endif + + #define UNREACHABLE_RETURN +#endif + +// SN Systems ProDG +#if defined(__SNC__) || defined(SNSYS) + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define CALLEE_POPS_HIDDEN_RETURN_POINTER + #define COMPLEX_OBJS_PASSED_BY_REF + + #ifdef __psp2__ + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #else + #define GNU_STYLE_VIRTUAL_METHOD + #define ASM_AT_N_T // AT&T style inline assembly + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #endif + + #define AS_SIZEOF_BOOL 1 + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + + // SN doesnt seem to like STDCALL. + // Maybe it can work with some fiddling, but I can't imagine linking to + // any STDCALL functions with a console anyway... + #define STDCALL + + // Linux specific + #ifdef __linux__ + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #endif + + // Support native calling conventions on x86, but not 64bit yet + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + #define AS_X86 + // PS3 + #elif (defined(__PPC__) || defined(__ppc__)) && defined(__PPU__) + // Support native calling conventions on PS3 + #define AS_PS3 + #define AS_PPC_64 + #define AS_NO_MEMORY_H + #define AS_NO_EXCEPTIONS + #include + // PSP + #elif defined(__psp__) + #define AS_NO_MEMORY_H + #define AS_MIPS + #define AS_PSP + #define AS_USE_DOUBLE_AS_FLOAT + // PSVita + #elif defined(__psp2__) + #define AS_PSVITA + #define AS_ARM + #define AS_NO_MEMORY_H + #define AS_NO_EXCEPTIONS + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #endif + + #define UNREACHABLE_RETURN +#endif + +// GNU C (and MinGW or Cygwin on Windows) +// Use the following command to determine predefined macros: echo . | g++ -dM -E - +// MSVC2015 can now use CLang too, but it shouldn't go in here +#if (defined(__GNUC__) && !defined(__SNC__) && !defined(_MSC_VER)) || defined(EPPC) || defined(__CYGWIN__) // JWC -- use this instead for Wii + #define GNU_STYLE_VIRTUAL_METHOD + #define MULTI_BASE_OFFSET(x) (*((asPWORD*)(&x)+1)) + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + #define CALLEE_POPS_HIDDEN_RETURN_POINTER + #define COMPLEX_OBJS_PASSED_BY_REF + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_ARRAY) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_ARRAY) + #define AS_NO_MEMORY_H + #define AS_SIZEOF_BOOL 1 + #define STDCALL __attribute__((stdcall)) + #define ASM_AT_N_T + + // WII U + #if defined(__ghs__) + #define AS_WIIU + + // Native calling conventions are not yet supported + #define AS_MAX_PORTABILITY + + // Marmalade is a cross platform SDK. It uses g++ to compile for iOS and Android + #elif defined(__S3E__) + #ifndef AS_MARMALADE + // From now on we'll use the below define + #define AS_MARMALADE + #endif + + // STDCALL is not available on Marmalade when compiled for iOS or Android + #undef STDCALL + #define STDCALL + + // Marmalade doesn't seem to have proper support for + // atomic instructions or read/write locks + #define AS_NO_THREADS + #define AS_NO_ATOMIC + + // Identify for which CPU the library is being built + #if defined(I3D_ARCH_X86) + #define AS_X86 + #elif defined(I3D_ARCH_ARM) + #define AS_ARM + + #define AS_SOFTFP + + // Marmalade appear to use the same ABI as Android when built for ARM + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #undef GNU_STYLE_VIRTUAL_METHOD + + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #endif + + // MacOSX and IPhone + #elif defined(__APPLE__) + + #include + + // Is this a Mac or an IPhone (or other iOS device)? + #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 + #define AS_IPHONE + #else + #define AS_MAC + #endif + + // The sizeof bool is different depending on the target CPU + #undef AS_SIZEOF_BOOL + #if defined(__ppc__) + #define AS_SIZEOF_BOOL 4 + // STDCALL is not available on PPC + #undef STDCALL + #define STDCALL + #else + #define AS_SIZEOF_BOOL 1 + #endif + + #if (defined(_ARM_) || defined(__arm__)) + // iOS use ARM processor + #define AS_ARM + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef GNU_STYLE_VIRTUAL_METHOD + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define COMPLEX_OBJS_PASSED_BY_REF + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + // iOS uses soft-float ABI + #define AS_SOFTFP + + // STDCALL is not available on ARM + #undef STDCALL + #define STDCALL + + #elif (defined(__arm64__)) + // The IPhone 5S+ uses an ARM64 processor + + // AngelScript currently doesn't support native calling + // for 64bit ARM processors so it's necessary to turn on + // portability mode + #define AS_MAX_PORTABILITY + + // STDCALL is not available on ARM + #undef STDCALL + #define STDCALL + + #elif (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + // Support native calling conventions on Mac OS X + Intel 32bit CPU + #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #elif defined(__LP64__) && !defined(__ppc__) && !defined(__PPC__) && !defined(__arm64__) + // http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html#//apple_ref/doc/uid/TP40005035-SW1 + #define AS_X64_GCC + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define HAS_128_BIT_PRIMITIVES + #define SPLIT_OBJS_BY_MEMBER_TYPES + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #define AS_LARGE_OBJS_PASSED_BY_REF + #define AS_LARGE_OBJ_MIN_SIZE 5 + // STDCALL is not available on 64bit Mac + #undef STDCALL + #define STDCALL + + #elif (defined(__ppc__) || defined(__PPC__)) && !defined(__LP64__) + // Support native calling conventions on Mac OS X + PPC 32bit CPU + #define AS_PPC + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #elif (defined(__ppc__) || defined(__PPC__)) && defined(__LP64__) + #define AS_PPC_64 + #else + // Unknown CPU type + #define AS_MAX_PORTABILITY + #endif + #define AS_POSIX_THREADS + + // Windows + #elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + // On Windows the simple classes are returned in the EAX:EDX registers + //#define THISCALL_RETURN_SIMPLE_IN_MEMORY + //#define CDECL_RETURN_SIMPLE_IN_MEMORY + //#define STDCALL_RETURN_SIMPLE_IN_MEMORY + + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + // Support native calling conventions on Intel 32bit CPU + #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + + // As of version 4.7 MinGW changed the ABI, presumably + // to be better aligned with how MSVC works + #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4 + #define AS_MINGW47 + #endif + + #if (__clang_major__ == 3 && __clang_minor__ > 4) || __clang_major > 3 + #define AS_MINGW47 + #endif + + #ifdef AS_MINGW47 + #undef CALLEE_POPS_HIDDEN_RETURN_POINTER + #define THISCALL_CALLEE_POPS_ARGUMENTS + #else + // Earlier versions than 4.7 + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #endif + + #elif defined(__x86_64__) + #define AS_X64_MINGW + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define AS_LARGE_OBJS_PASSED_BY_REF + #define AS_LARGE_OBJ_MIN_SIZE 3 + #define COMPLEX_OBJS_PASSED_BY_REF + #else + #define AS_MAX_PORTABILITY + #endif + #define AS_WIN + #define AS_WINDOWS_THREADS + + // Linux + #elif defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) + + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + // x86 32bit + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + + // Support native calling conventions on Intel 32bit CPU + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #elif defined(__x86_64__) + // x86 64bit + #define AS_X64_GCC + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define HAS_128_BIT_PRIMITIVES + #define SPLIT_OBJS_BY_MEMBER_TYPES + #define AS_LARGE_OBJS_PASSED_BY_REF + #define AS_LARGE_OBJ_MIN_SIZE 5 + // STDCALL is not available on 64bit Linux + #undef STDCALL + #define STDCALL + #elif defined(__ARMEL__) || defined(__arm__) || defined(__aarch64__) || defined(__AARCH64EL__) + // arm + + // The assembler code currently doesn't support arm v4, nor 64bit (v8) + #if !defined(__ARM_ARCH_4__) && !defined(__ARM_ARCH_4T__) && !defined(__LP64__) + #define AS_ARM + + // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S + #define AS_NO_EXCEPTIONS + + #undef STDCALL + #define STDCALL + + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #ifndef AS_MAX_PORTABILITY + // Make a few checks against incompatible ABI combinations + #if defined(__FAST_MATH__) && __FAST_MATH__ == 1 + #error -ffast-math is not supported with native calling conventions + #endif + #endif + + // Verify if soft-float or hard-float ABI is used + #if defined(__SOFTFP__) && __SOFTFP__ == 1 + // -ffloat-abi=softfp or -ffloat-abi=soft + #define AS_SOFTFP + #endif + + // Tested with both hard float and soft float abi + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #endif + + #elif defined(__mips__) + // mips + #define AS_MIPS + #undef STDCALL + #define STDCALL + + #ifdef _ABIO32 + // 32bit O32 ABI + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif + #elif defined(__PPC64__) + // PPC 64bit + + // The code in as_callfunc_ppc_64.cpp was built for PS3 and XBox 360, that + // although use 64bit PPC only uses 32bit pointers. + // TODO: Add support for native calling conventions on Linux with PPC 64bit + #define AS_MAX_PORTABILITY + #else + #define AS_MAX_PORTABILITY + #endif + #define AS_LINUX + #define AS_POSIX_THREADS + + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + + // Free BSD + #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) + #define AS_BSD + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define AS_X86 + #elif defined(__LP64__) + #define AS_X64_GCC + #define HAS_128_BIT_PRIMITIVES + #define SPLIT_OBJS_BY_MEMBER_TYPES + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #define AS_LARGE_OBJS_PASSED_BY_REF + #define AS_LARGE_OBJ_MIN_SIZE 5 + #undef STDCALL + #define STDCALL + #else + #define AS_MAX_PORTABILITY + #endif + #define AS_POSIX_THREADS + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + + // PSP and PS2 + #elif defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2) + // Support native calling conventions on MIPS architecture + #if (defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__)) && !defined(__LP64__) + #define AS_MIPS + #define AS_USE_DOUBLE_AS_FLOAT + #else + #define AS_MAX_PORTABILITY + #endif + + // PS3 + #elif (defined(__PPC__) || defined(__ppc__)) && defined(__PPU__) + // Support native calling conventions on PS3 + #define AS_PS3 + #define AS_PPC_64 + #define SPLIT_OBJS_BY_MEMBER_TYPES + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + // PS3 doesn't have STDCALL + #undef STDCALL + #define STDCALL + + // Dreamcast + #elif __SH4_SINGLE_ONLY__ + // Support native calling conventions on Dreamcast + #define AS_DC + #define AS_SH4 + + // Wii JWC - Close to PS3 just no PPC_64 and AS_PS3 + #elif defined(EPPC) + #define AS_WII + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #undef STDCALL + #define STDCALL + + // Android + #elif defined(ANDROID) || defined(__ANDROID__) + #define AS_ANDROID + + // Android 2.3+ supports posix threads + #define AS_POSIX_THREADS + + // Common configuration with Android arm and x86 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + + #if (defined(_ARM_) || defined(__arm__)) + // Android ARM + + // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S + #define AS_NO_EXCEPTIONS + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + // The stdcall calling convention is not used on the arm cpu + #undef STDCALL + #define STDCALL + + #undef GNU_STYLE_VIRTUAL_METHOD + + #define AS_ARM + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #define AS_SOFTFP + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #elif (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + // Android Intel x86 (same config as Linux x86). Tested with Intel x86 Atom System Image. + + // Support native calling conventions on Intel 32bit CPU + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #elif defined(__mips__) + #define AS_MIPS + #undef STDCALL + #define STDCALL + + #ifdef _ABIO32 + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif + #endif + + // Haiku OS + #elif __HAIKU__ + #define AS_HAIKU + // Only x86-32 is currently supported by Haiku, but they do plan to support + // x86-64 and PowerPC in the future, so should go ahead and check the platform + // for future compatibility + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + #define AS_X86 + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #else + #define AS_MAX_PORTABILITY + #endif + + #define AS_POSIX_THREADS + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + + // Illumos + #elif defined(__sun) + #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + + // Support native calling conventions on Intel 32bit CPU + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define AS_X86 + #elif defined(__x86_64__) + #define AS_X64_GCC + #define HAS_128_BIT_PRIMITIVES + #define SPLIT_OBJS_BY_MEMBER_TYPES + // STDCALL is not available on 64bit Linux + #undef STDCALL + #define STDCALL + #else + #define AS_MAX_PORTABILITY + #endif + #define AS_ILLUMOS + #define AS_POSIX_THREADS + + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + #endif + + #define UNREACHABLE_RETURN +#endif + +// Sun CC +// Initial information provided by Andrey Bergman +#if defined(__SUNPRO_CC) + #if defined(__sparc) + #define AS_SPARC + #endif + + #if defined(__sun) + #define AS_SUN + #endif + + // Native calling conventions is not yet supported for Sun CC + #if !defined(AS_MAX_PORTABILITY) + #define AS_MAX_PORTABILITY + #endif + + // I presume Sun CC uses a similar structure of method pointers as gnuc + #define MULTI_BASE_OFFSET(x) (*((asPWORD*)(&x)+1)) + + #if !defined(AS_SIZEOF_BOOL) + #define AS_SIZEOF_BOOL 1 // sizeof(bool) == 1 + #endif + #if !defined(UNREACHABLE_RETURN) + #define UNREACHABLE_RETURN + #endif + #if !defined(STDCALL) + #define STDCALL // There is no stdcall on Solaris/SunPro/SPARC + #endif + #if !defined(asVSNPRINTF) + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + #endif +#endif + + +// +// Detect target hardware +//------------------------------------------------ + +// Big endian CPU target? +// see: http://sourceforge.net/p/predef/wiki/Endianness/ +#if !defined(AS_BIG_ENDIAN) && \ + defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) + #define AS_BIG_ENDIAN +#endif + +// Dreamcast and Gamecube use only 32bit floats, so treat doubles as floats +#if defined(__SH4_SINGLE_ONLY__) || defined(_GC) + #define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles +#endif + +// If there are no current support for native calling +// conventions, then compile with AS_MAX_PORTABILITY +#if (!defined(AS_X86) && !defined(AS_SH4) && !defined(AS_MIPS) && !defined(AS_PPC) && !defined(AS_PPC_64) && !defined(AS_XENON) && !defined(AS_X64_GCC) && !defined(AS_X64_MSVC) && !defined(AS_ARM) && !defined(AS_X64_MINGW)) + #ifndef AS_MAX_PORTABILITY + #define AS_MAX_PORTABILITY + #endif +#endif + +// If the platform doesn't support atomic instructions we can't allow +// multithreading as the reference counters won't be threadsafe +#if defined(AS_NO_ATOMIC) && !defined(AS_NO_THREADS) + #define AS_NO_THREADS +#endif + +// If the form of threads to use hasn't been chosen +// then the library will be compiled without support +// for multithreading +#if !defined(AS_POSIX_THREADS) && !defined(AS_WINDOWS_THREADS) + #define AS_NO_THREADS +#endif + + +// The assert macro +#if defined(ANDROID) + #if defined(AS_DEBUG) + #include + #include + #define asASSERT(x) \ + do { \ + if (!(x)) { \ + __android_log_print(ANDROID_LOG_ERROR, "AngelScript", "Assert failed at %s:%d - %s", __FILE__, __LINE__, #x); \ + exit(1); \ + } \ + } while (0) + #else + #define asASSERT(x) + #endif +#else + #include + #define asASSERT(x) assert(x) +#endif + + + +// +// Internal defines (do not change these) +//---------------------------------------------------------------- + +#define ARG_W(b) ((asWORD*)&b) +#define ARG_DW(b) ((asDWORD*)&b) +#define ARG_QW(b) ((asQWORD*)&b) +#define ARG_PTR(b) ((asPWORD*)&b) +#define BCARG_W(b) ((asWORD*)&(b)[1]) +#define BCARG_DW(b) ((asDWORD*)&(b)[1]) +#define BCARG_QW(b) ((asQWORD*)&(b)[1]) +#define BCARG_PTR(b) ((asPWORD*)&(b)[1]) + +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (void)(x) + +#include "../include/angelscript.h" +#include "as_memory.h" + +#ifdef AS_USE_NAMESPACE +using namespace AngelScript; +#endif + +#endif diff --git a/3rdparty/angelscript/include/as_configgroup.h b/3rdparty/angelscript/include/as_configgroup.h new file mode 100644 index 0000000..4872030 --- /dev/null +++ b/3rdparty/angelscript/include/as_configgroup.h @@ -0,0 +1,84 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_configgroup.h +// +// This class holds configuration groups for the engine +// + + + +#ifndef AS_CONFIGGROUP_H +#define AS_CONFIGGROUP_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_objecttype.h" + +BEGIN_AS_NAMESPACE + +class asCConfigGroup +{ +public: + asCConfigGroup(); + ~asCConfigGroup(); + + // Memory management + int AddRef(); + int Release(); + + asCTypeInfo *FindType(const char *name); + void RefConfigGroup(asCConfigGroup *group); + + bool HasLiveObjects(); + void RemoveConfiguration(asCScriptEngine *engine, bool notUsed = false); + + void AddReferencesForFunc(asCScriptEngine *engine, asCScriptFunction *func); + void AddReferencesForType(asCScriptEngine *engine, asCTypeInfo *type); + + asCString groupName; + int refCount; + + asCArray types; + asCArray scriptFunctions; + asCArray globalProps; + asCArray referencedConfigGroups; + + // This array holds the generated template instances that are used + // by the config group as part of function signature or property + asCArray generatedTemplateInstances; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_context.h b/3rdparty/angelscript/include/as_context.h new file mode 100644 index 0000000..8396067 --- /dev/null +++ b/3rdparty/angelscript/include/as_context.h @@ -0,0 +1,245 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_context.h +// +// This class handles the execution of the byte code +// + + +#ifndef AS_CONTEXT_H +#define AS_CONTEXT_H + +#include "as_config.h" +#include "as_atomic.h" +#include "as_array.h" +#include "as_string.h" +#include "as_objecttype.h" +#include "as_callfunc.h" + +BEGIN_AS_NAMESPACE + +class asCScriptFunction; +class asCScriptEngine; + +class asCContext : public asIScriptContext +{ +public: + // Memory management + int AddRef() const; + int Release() const; + + // Miscellaneous + asIScriptEngine *GetEngine() const; + + // Execution + int Prepare(asIScriptFunction *func); + int Unprepare(); + int Execute(); + int Abort(); + int Suspend(); + asEContextState GetState() const; + int PushState(); + int PopState(); + bool IsNested(asUINT *nestCount = 0) const; + + // Object pointer for calling class methods + int SetObject(void *obj); + + // Arguments + int SetArgByte(asUINT arg, asBYTE value); + int SetArgWord(asUINT arg, asWORD value); + int SetArgDWord(asUINT arg, asDWORD value); + int SetArgQWord(asUINT arg, asQWORD value); + int SetArgFloat(asUINT arg, float value); + int SetArgDouble(asUINT arg, double value); + int SetArgAddress(asUINT arg, void *addr); + int SetArgObject(asUINT arg, void *obj); + int SetArgVarType(asUINT arg, void *ptr, int typeId); + void *GetAddressOfArg(asUINT arg); + + // Return value + asBYTE GetReturnByte(); + asWORD GetReturnWord(); + asDWORD GetReturnDWord(); + asQWORD GetReturnQWord(); + float GetReturnFloat(); + double GetReturnDouble(); + void *GetReturnAddress(); + void *GetReturnObject(); + void *GetAddressOfReturnValue(); + + // Exception handling + int SetException(const char *descr); + int GetExceptionLineNumber(int *column, const char **sectionName); + asIScriptFunction *GetExceptionFunction(); + const char * GetExceptionString(); + int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv); + void ClearExceptionCallback(); + + // Debugging + int SetLineCallback(asSFuncPtr callback, void *obj, int callConv); + void ClearLineCallback(); + asUINT GetCallstackSize() const; + asIScriptFunction *GetFunction(asUINT stackLevel); + int GetLineNumber(asUINT stackLevel, int *column, const char **sectionName); + int GetVarCount(asUINT stackLevel); + const char *GetVarName(asUINT varIndex, asUINT stackLevel); + const char *GetVarDeclaration(asUINT varIndex, asUINT stackLevel, bool includeNamespace); + int GetVarTypeId(asUINT varIndex, asUINT stackLevel); + void *GetAddressOfVar(asUINT varIndex, asUINT stackLevel); + bool IsVarInScope(asUINT varIndex, asUINT stackLevel); + int GetThisTypeId(asUINT stackLevel); + void *GetThisPointer(asUINT stackLevel); + asIScriptFunction *GetSystemFunction(); + + // User data + void *SetUserData(void *data, asPWORD type); + void *GetUserData(asPWORD type) const; + +public: + // Internal public functions + asCContext(asCScriptEngine *engine, bool holdRef); + virtual ~asCContext(); + +//protected: + friend class asCScriptEngine; + + void CallLineCallback(); + void CallExceptionCallback(); + + int CallGeneric(asCScriptFunction *func); + + void DetachEngine(); + + void ExecuteNext(); + void CleanStack(); + void CleanStackFrame(); + void CleanArgsOnStack(); + void CleanReturnObject(); + void DetermineLiveObjects(asCArray &liveObjects, asUINT stackLevel); + + void PushCallState(); + void PopCallState(); + void CallScriptFunction(asCScriptFunction *func); + void CallInterfaceMethod(asCScriptFunction *func); + void PrepareScriptFunction(); + + bool ReserveStackSpace(asUINT size); + + void SetInternalException(const char *descr); + + // Must be protected for multiple accesses + mutable asCAtomic m_refCount; + + bool m_holdEngineRef; + asCScriptEngine *m_engine; + + asEContextState m_status; + bool m_doSuspend; + bool m_doAbort; + bool m_externalSuspendRequest; + + asCScriptFunction *m_currentFunction; + asCScriptFunction *m_callingSystemFunction; + + // The call stack holds program pointer, stack pointer, etc for caller functions + asCArray m_callStack; + + // Dynamically growing local stack + asCArray m_stackBlocks; + asUINT m_stackBlockSize; + asUINT m_stackIndex; + asDWORD *m_originalStackPointer; + + // Exception handling + bool m_isStackMemoryNotAllocated; + bool m_needToCleanupArgs; + bool m_inExceptionHandler; + asCString m_exceptionString; + int m_exceptionFunction; + int m_exceptionSectionIdx; + int m_exceptionLine; + int m_exceptionColumn; + + // The last prepared function, and some cached values related to it + asCScriptFunction *m_initialFunction; + int m_returnValueSize; + int m_argumentsSize; + + // callbacks + bool m_lineCallback; + asSSystemFunctionInterface m_lineCallbackFunc; + void * m_lineCallbackObj; + + bool m_exceptionCallback; + asSSystemFunctionInterface m_exceptionCallbackFunc; + void * m_exceptionCallbackObj; + + asCArray m_userData; + + // Registers available to JIT compiler functions + asSVMRegisters m_regs; +}; + +// TODO: Move these to as_utils.h +int as_powi(int base, int exponent, bool& isOverflow); +asDWORD as_powu(asDWORD base, asDWORD exponent, bool& isOverflow); +asINT64 as_powi64(asINT64 base, asINT64 exponent, bool& isOverflow); +asQWORD as_powu64(asQWORD base, asQWORD exponent, bool& isOverflow); + +// Optional template version of powi if overflow detection is not used. +#if 0 +template +T as_powi(T base, T exponent) +{ + // Test for sign bit (huge number is OK) + if( exponent & (T(1)<<(sizeof(T)*8-1)) ) + return 0; + else + { + int result = 1; + while( exponent ) + { + if( exponent & 1 ) + result *= base; + exponent >>= 1; + base *= base; + } + return result; + } +} +#endif + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_criticalsection.h b/3rdparty/angelscript/include/as_criticalsection.h new file mode 100644 index 0000000..0a5084a --- /dev/null +++ b/3rdparty/angelscript/include/as_criticalsection.h @@ -0,0 +1,187 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_criticalsection.h +// +// Classes for multi threading support +// + +#ifndef AS_CRITICALSECTION_H +#define AS_CRITICALSECTION_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +#ifdef AS_NO_THREADS + +#define DECLARECRITICALSECTION(x) +#define ENTERCRITICALSECTION(x) +#define LEAVECRITICALSECTION(x) + +inline bool tryEnter() { return true; } +#define TRYENTERCRITICALSECTION(x) tryEnter() + +#define DECLAREREADWRITELOCK(x) +#define ACQUIREEXCLUSIVE(x) +#define RELEASEEXCLUSIVE(x) +#define ACQUIRESHARED(x) +#define RELEASESHARED(x) + +#else + +#define DECLARECRITICALSECTION(x) asCThreadCriticalSection x; +#define ENTERCRITICALSECTION(x) x.Enter() +#define LEAVECRITICALSECTION(x) x.Leave() +#define TRYENTERCRITICALSECTION(x) x.TryEnter() + +#define DECLAREREADWRITELOCK(x) asCThreadReadWriteLock x; +#define ACQUIREEXCLUSIVE(x) x.AcquireExclusive() +#define RELEASEEXCLUSIVE(x) x.ReleaseExclusive() +#define ACQUIRESHARED(x) x.AcquireShared() +#define RELEASESHARED(x) x.ReleaseShared() + +#ifdef AS_POSIX_THREADS + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +class asCThreadCriticalSection +{ +public: + asCThreadCriticalSection(); + ~asCThreadCriticalSection(); + + void Enter(); + void Leave(); + bool TryEnter(); + +protected: + pthread_mutex_t cs; +}; + +class asCThreadReadWriteLock +{ +public: + asCThreadReadWriteLock(); + ~asCThreadReadWriteLock(); + + void AcquireExclusive(); + void ReleaseExclusive(); + bool TryAcquireExclusive(); + + void AcquireShared(); + void ReleaseShared(); + bool TryAcquireShared(); + +protected: + pthread_rwlock_t lock; +}; + +#elif defined(AS_WINDOWS_THREADS) + +END_AS_NAMESPACE +#ifdef AS_XBOX360 +#include +#else +#define WIN32_LEAN_AND_MEAN +#ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0600 // We need this to get the declaration for Windows Phone compatible Ex functions +#endif +#include +#endif +BEGIN_AS_NAMESPACE + +// Undefine macros that cause problems in our code +#undef GetObject +#undef RegisterClass + +class asCThreadCriticalSection +{ +public: + asCThreadCriticalSection(); + ~asCThreadCriticalSection(); + + void Enter(); + void Leave(); + bool TryEnter(); + +protected: + CRITICAL_SECTION cs; +}; + +class asCThreadReadWriteLock +{ +public: + asCThreadReadWriteLock(); + ~asCThreadReadWriteLock(); + + void AcquireExclusive(); + void ReleaseExclusive(); + + void AcquireShared(); + void ReleaseShared(); + +protected: + // The Slim Read Write Lock object, SRWLOCK, is more efficient + // but it is only available from Windows Vista so we cannot use it and + // maintain compatibility with olders versions of Windows. + + // Critical sections and semaphores are available on Windows XP and onwards. + // Windows XP is oldest version we support with multithreading. + + // The implementation is based on the following article, that shows + // how to implement a fair read/write lock that doesn't risk starving + // the writers: + + // http://doc.qt.nokia.com/qq/qq11-mutex.html + + // TODO: Allow use of SRWLOCK through configuration in as_config.h + + CRITICAL_SECTION writeLock; + HANDLE readLocks; +}; + +// This constant really should be a member of asCThreadReadWriteLock, +// but it gives a compiler error on MSVC6 so I'm leaving it outside +static const asUINT maxReaders = 10; + +#endif + +#endif + +END_AS_NAMESPACE + +#endif + diff --git a/3rdparty/angelscript/include/as_datatype.h b/3rdparty/angelscript/include/as_datatype.h new file mode 100644 index 0000000..665f83a --- /dev/null +++ b/3rdparty/angelscript/include/as_datatype.h @@ -0,0 +1,161 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_datatype.h +// +// This class describes the datatype for expressions during compilation +// + + + +#ifndef AS_DATATYPE_H +#define AS_DATATYPE_H + +#include "as_tokendef.h" +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +struct asSTypeBehaviour; +class asCScriptEngine; +class asCTypeInfo; +class asCScriptFunction; +class asCModule; +class asCObjectType; +class asCEnumType; +struct asSNameSpace; + +// TODO: refactor: Reference should not be part of the datatype. This should be stored separately, e.g. in asCExprValue +// MakeReference, MakeReadOnly, IsReference, IsReadOnly should be removed + +class asCDataType +{ +public: + asCDataType(); + asCDataType(const asCDataType &); + ~asCDataType(); + + bool IsValid() const; + + asCString Format(asSNameSpace *currNs, bool includeNamespace = false) const; + + static asCDataType CreatePrimitive(eTokenType tt, bool isConst); + static asCDataType CreateType(asCTypeInfo *ti, bool isConst); + static asCDataType CreateAuto(bool isConst); + static asCDataType CreateObjectHandle(asCTypeInfo *ot, bool isConst); + static asCDataType CreateNullHandle(); + + int MakeHandle(bool b, bool acceptHandleForScope = false); + int MakeArray(asCScriptEngine *engine, asCModule *requestingModule); + int MakeReference(bool b); + int MakeReadOnly(bool b); + int MakeHandleToConst(bool b); + void SetIfHandleThenConst(bool b) { ifHandleThenConst = b; } + bool HasIfHandleThenConst() const { return ifHandleThenConst; } + + bool IsTemplate() const; + bool IsScriptObject() const; + bool IsPrimitive() const; + bool IsMathType() const; + bool IsObject() const; + bool IsReference() const {return isReference;} + bool IsAuto() const {return isAuto;} + bool IsReadOnly() const; + bool IsIntegerType() const; + bool IsUnsignedType() const; + bool IsFloatType() const; + bool IsDoubleType() const; + bool IsBooleanType() const; + bool IsObjectHandle() const {return isObjectHandle;} + bool IsHandleToAuto() const {return isAuto && isObjectHandle;} + bool IsHandleToConst() const; + bool IsArrayType() const; + bool IsEnumType() const; + bool IsAnyType() const {return tokenType == ttQuestion;} + bool IsHandleToAsHandleType() const {return isHandleToAsHandleType;} + bool IsAbstractClass() const; + bool IsInterface() const; + bool IsFuncdef() const; + + bool IsObjectConst() const; + + bool IsEqualExceptRef(const asCDataType &) const; + bool IsEqualExceptRefAndConst(const asCDataType &) const; + bool IsEqualExceptConst(const asCDataType &) const; + bool IsNullHandle() const; + + bool SupportHandles() const; + bool CanBeInstantiated() const; + bool CanBeCopied() const; + + bool operator ==(const asCDataType &) const; + bool operator !=(const asCDataType &) const; + + asCDataType GetSubType(asUINT subtypeIndex = 0) const; + eTokenType GetTokenType() const {return tokenType;} + asCTypeInfo *GetTypeInfo() const { return typeInfo; } + + int GetSizeOnStackDWords() const; + int GetSizeInMemoryBytes() const; + int GetSizeInMemoryDWords() const; +#ifdef WIP_16BYTE_ALIGN + int GetAlignment() const; +#endif + + void SetTokenType(eTokenType tt) {tokenType = tt;} + void SetTypeInfo(asCTypeInfo *ti) {typeInfo = ti;} + + asCDataType &operator =(const asCDataType &); + + asSTypeBehaviour *GetBehaviour() const; + +protected: + // Base object type + eTokenType tokenType; + + // Behaviour type + asCTypeInfo *typeInfo; + + // Top level + bool isReference:1; + bool isReadOnly:1; + bool isObjectHandle:1; + bool isConstHandle:1; + bool isAuto:1; + bool isHandleToAsHandleType:1; // Used by the compiler to know how to initialize the object + bool ifHandleThenConst:1; // Used when creating template instances to determine if a handle should be const or not + char dummy:1; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_debug.h b/3rdparty/angelscript/include/as_debug.h new file mode 100644 index 0000000..e67d8b3 --- /dev/null +++ b/3rdparty/angelscript/include/as_debug.h @@ -0,0 +1,270 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_debug.h +// + +#ifndef AS_DEBUG_H +#define AS_DEBUG_H + +#include "as_config.h" + +#if defined(AS_DEBUG) + +#ifndef AS_WII +// The Wii SDK doesn't have these, we'll survive without AS_DEBUG + +#ifndef _WIN32_WCE +// Neither does WinCE + +#ifndef AS_PSVITA +// Possible on PSVita, but requires SDK access + +#if !defined(_MSC_VER) && (defined(__GNUC__) || defined(AS_MARMALADE)) + +#ifdef __ghs__ +// WIIU defines __GNUC__ but types are not defined here in 'conventional' way +#include +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +typedef float float32_t; +typedef double float64_t; +#else +// Define mkdir for GNUC +#include +#include +#define _mkdir(dirname) mkdir(dirname, S_IRWXU) +#endif +#else +#include +#endif + +#endif // AS_PSVITA +#endif // _WIN32_WCE +#endif // AS_WII + +#endif // !defined(AS_DEBUG) + + + +#if defined(_MSC_VER) && defined(AS_PROFILE) +// Currently only do profiling with MSVC++ + +#include +#include +#include "as_string.h" +#include "as_map.h" +#include "as_string_util.h" + +BEGIN_AS_NAMESPACE + +struct TimeCount +{ + double time; + int count; + double max; + double min; +}; + +class CProfiler +{ +public: + CProfiler() + { + // We need to know how often the clock is updated + __int64 tps; + if( !QueryPerformanceFrequency((LARGE_INTEGER *)&tps) ) + usePerformance = false; + else + { + usePerformance = true; + ticksPerSecond = double(tps); + } + + timeOffset = GetTime(); + } + + ~CProfiler() + { + WriteSummary(); + } + + double GetTime() + { + if( usePerformance ) + { + __int64 ticks; + QueryPerformanceCounter((LARGE_INTEGER *)&ticks); + + return double(ticks)/ticksPerSecond - timeOffset; + } + + return double(timeGetTime())/1000.0 - timeOffset; + } + + double Begin(const char *name) + { + double time = GetTime(); + + // Add the scope to the key + if( key.GetLength() ) + key += "|"; + key += name; + + // Compensate for the time spent writing to the file + timeOffset += GetTime() - time; + + return time; + } + + void End(const char * /*name*/, double beginTime) + { + double time = GetTime(); + + double elapsed = time - beginTime; + + // Update the profile info for this scope + asSMapNode *cursor; + if( map.MoveTo(&cursor, key) ) + { + cursor->value.time += elapsed; + cursor->value.count++; + if( cursor->value.max < elapsed ) + cursor->value.max = elapsed; + if( cursor->value.min > elapsed ) + cursor->value.min = elapsed; + } + else + { + TimeCount tc = {elapsed, 1, elapsed, elapsed}; + map.Insert(key, tc); + } + + // Remove the inner most scope from the key + int n = key.FindLast("|"); + if( n > 0 ) + key.SetLength(n); + else + key.SetLength(0); + + // Compensate for the time spent writing to the file + timeOffset += GetTime() - time; + } + +protected: + void WriteSummary() + { + // Write the analyzed info into a file for inspection + _mkdir("AS_DEBUG"); + FILE *fp; + #if _MSC_VER >= 1500 && !defined(AS_MARMALADE) + fopen_s(&fp, "AS_DEBUG/profiling_summary.txt", "wt"); + #else + fp = fopen("AS_DEBUG/profiling_summary.txt", "wt"); + #endif + if( fp == 0 ) + return; + + fprintf(fp, "%-60s %10s %15s %15s %15s %15s\n\n", "Scope", "Count", "Tot time", "Avg time", "Max time", "Min time"); + + asSMapNode *cursor; + map.MoveLast(&cursor); + while( cursor ) + { + asCString key = cursor->key; + int count; + int n = key.FindLast("|", &count); + if( count ) + { + key = asCString(" ", count) + key.SubString(n+1); + } + + fprintf(fp, "%-60s %10d %15.6f %15.6f %15.6f %15.6f\n", key.AddressOf(), cursor->value.count, cursor->value.time, cursor->value.time / cursor->value.count, cursor->value.max, cursor->value.min); + + map.MovePrev(&cursor, cursor); + } + + fclose(fp); + } + + double timeOffset; + double ticksPerSecond; + bool usePerformance; + + asCString key; + asCMap map; +}; + +extern CProfiler g_profiler; + +class CProfilerScope +{ +public: + CProfilerScope(const char *name) + { + this->name = name; + beginTime = g_profiler.Begin(name); + } + + ~CProfilerScope() + { + g_profiler.End(name, beginTime); + } + +protected: + const char *name; + double beginTime; +}; + +#define TimeIt(x) CProfilerScope profilescope(x) + +END_AS_NAMESPACE + +#else // !(_MSC_VER && AS_PROFILE) + +// Define it so nothing is done +#define TimeIt(x) + +#endif // !(_MSC_VER && AS_PROFILE) + + + + +#endif // defined(AS_DEBUG_H) + + diff --git a/3rdparty/angelscript/include/as_gc.h b/3rdparty/angelscript/include/as_gc.h new file mode 100644 index 0000000..db8da0e --- /dev/null +++ b/3rdparty/angelscript/include/as_gc.h @@ -0,0 +1,147 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_gc.h +// +// The garbage collector is used to resolve cyclic references +// + + + +#ifndef AS_GC_H +#define AS_GC_H + +#include "as_config.h" +#include "as_array.h" +#include "as_map.h" +#include "as_thread.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCObjectType; + +class asCGarbageCollector +{ +public: + asCGarbageCollector(); + ~asCGarbageCollector(); + + int GarbageCollect(asDWORD flags, asUINT iterations); + void GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const; + void GCEnumCallback(void *reference); + int AddScriptObjectToGC(void *obj, asCObjectType *objType); + int GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asITypeInfo **type); + + int ReportAndReleaseUndestroyedObjects(); + + asCScriptEngine *engine; + +protected: + struct asSObjTypePair {void *obj; asCObjectType *type; asUINT seqNbr;}; + struct asSIntTypePair {int i; asCObjectType *type;}; + typedef asSMapNode asSMapNode_t; + + enum egcDestroyState + { + destroyGarbage_init = 0, + destroyGarbage_loop, + destroyGarbage_haveMore + }; + + enum egcDetectState + { + clearCounters_init = 0, + clearCounters_loop, + buildMap_init, + buildMap_loop, + countReferences_init, + countReferences_loop, + detectGarbage_init, + detectGarbage_loop1, + detectGarbage_loop2, + verifyUnmarked_init, + verifyUnmarked_loop, + breakCircles_init, + breakCircles_loop, + breakCircles_haveGarbage + }; + + int DestroyNewGarbage(); + int DestroyOldGarbage(); + int IdentifyGarbageWithCyclicRefs(); + asSObjTypePair GetNewObjectAtIdx(int idx); + asSObjTypePair GetOldObjectAtIdx(int idx); + void RemoveNewObjectAtIdx(int idx); + void RemoveOldObjectAtIdx(int idx); + void MoveObjectToOldList(int idx); + void MoveAllObjectsToOldList(); + + // Holds all the objects known by the garbage collector + asCArray gcNewObjects; + asCArray gcOldObjects; + + // This array temporarily holds references to objects known to be live objects + asCArray liveObjects; + + // This map holds objects currently being searched for cyclic references, it also holds a + // counter that gives the number of references to the object that the GC can't reach + asCMap gcMap; + + // State variables + egcDestroyState destroyNewState; + egcDestroyState destroyOldState; + asUINT destroyNewIdx; + asUINT destroyOldIdx; + asUINT numDestroyed; + asUINT numNewDestroyed; + egcDetectState detectState; + asUINT detectIdx; + asUINT numDetected; + asUINT numAdded; + asUINT seqAtSweepStart[3]; + asSMapNode_t *gcMapCursor; + bool isProcessing; + + // We'll keep a pool of nodes to avoid allocating memory all the time + asSMapNode_t *GetNode(void *obj, asSIntTypePair it); + void ReturnNode(asSMapNode_t *node); + asCArray freeNodes; + + // Critical section for multithreaded access + DECLARECRITICALSECTION(gcCritical) // Used for adding/removing objects + DECLARECRITICALSECTION(gcCollecting) // Used for processing +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_generic.h b/3rdparty/angelscript/include/as_generic.h new file mode 100644 index 0000000..158d8f9 --- /dev/null +++ b/3rdparty/angelscript/include/as_generic.h @@ -0,0 +1,108 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_generic.h +// +// This class handles the call to a function registered with asCALL_GENERIC +// + + +#ifndef AS_GENERIC_H +#define AS_GENERIC_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCScriptFunction; + +class asCGeneric : public asIScriptGeneric +{ +public: +//------------------------------ +// asIScriptGeneric +//------------------------------ + // Miscellaneous + asIScriptEngine *GetEngine() const; + asIScriptFunction *GetFunction() const; + void *GetAuxiliary() const; + + // Object + void *GetObject(); + int GetObjectTypeId() const; + + // Arguments + int GetArgCount() const; + int GetArgTypeId(asUINT arg, asDWORD *flags = 0) const; + asBYTE GetArgByte(asUINT arg); + asWORD GetArgWord(asUINT arg); + asDWORD GetArgDWord(asUINT arg); + asQWORD GetArgQWord(asUINT arg); + float GetArgFloat(asUINT arg); + double GetArgDouble(asUINT arg); + void *GetArgAddress(asUINT arg); + void *GetArgObject(asUINT arg); + void *GetAddressOfArg(asUINT arg); + + // Return value + int GetReturnTypeId(asDWORD *flags = 0) const; + int SetReturnByte(asBYTE val); + int SetReturnWord(asWORD val); + int SetReturnDWord(asDWORD val); + int SetReturnQWord(asQWORD val); + int SetReturnFloat(float val); + int SetReturnDouble(double val); + int SetReturnAddress(void *addr); + int SetReturnObject(void *obj); + void *GetAddressOfReturnLocation(); + +//------------------------ +// internal +//------------------------- + asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer); + virtual ~asCGeneric(); + + void *GetReturnPointer(); + + asCScriptEngine *engine; + asCScriptFunction *sysFunction; + void *currentObject; + asDWORD *stackPointer; + void *objectRegister; + + asQWORD returnVal; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_map.h b/3rdparty/angelscript/include/as_map.h new file mode 100644 index 0000000..c21037b --- /dev/null +++ b/3rdparty/angelscript/include/as_map.h @@ -0,0 +1,786 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2013 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_map.h +// +// This class is used for mapping a value to another +// + + +#ifndef AS_MAP_H +#define AS_MAP_H + +template struct asSMapNode; + +template class asCMap +{ +public: + asCMap(); + ~asCMap(); + + int Insert(const KEY &key, const VAL &value); + int Insert(asSMapNode *node); + int GetCount() const; + + const KEY &GetKey(const asSMapNode *cursor) const; + const VAL &GetValue(const asSMapNode *cursor) const; + VAL &GetValue(asSMapNode *cursor); + + void Erase(asSMapNode *cursor); + asSMapNode *Remove(asSMapNode *cursor); + void EraseAll(); + + void SwapWith(asCMap &other); + + // Returns true as long as cursor is valid + + bool MoveTo(asSMapNode **out, const KEY &key) const; + bool MoveFirst(asSMapNode **out) const; + bool MoveLast(asSMapNode **out) const; + bool MoveNext(asSMapNode **out, asSMapNode *cursor) const; + bool MovePrev(asSMapNode **out, asSMapNode *cursor) const; + + // For debugging only + + int CheckIntegrity(asSMapNode *node) const; + +protected: + // Don't allow value assignment + asCMap &operator=(const asCMap &) { return *this; } + + void BalanceInsert(asSMapNode *node); + void BalanceErase(asSMapNode *child, asSMapNode *parent); + + int EraseAll(asSMapNode *node); + int RotateLeft(asSMapNode *node); + int RotateRight(asSMapNode *node); + + asSMapNode *root; + asSMapNode dummy; + + int count; +}; + +//--------------------------------------------------------------------------- +// Implementation + +// Properties of a Red-Black Tree +// +// 1. The root is always black +// 2. All single paths from the root to leafs +// contain the same amount of black nodes +// 3. No red node can have a red node as parent + +#define ISRED(x) ((x != 0) && (x)->isRed) +#define ISBLACK(x) (!ISRED(x)) + +template struct asSMapNode +{ + asSMapNode() {parent = 0; left = 0; right = 0; isRed = true;} + void Init(KEY k, VAL v) {key = k; value = v; parent = 0; left = 0; right = 0; isRed = true;} + + asSMapNode *parent; + asSMapNode *left; + asSMapNode *right; + bool isRed; + + KEY key; + VAL value; +}; + +template +asCMap::asCMap() +{ + root = 0; + count = 0; +} + +template +asCMap::~asCMap() +{ + EraseAll(); +} + +template +void asCMap::SwapWith(asCMap &other) +{ + asSMapNode *tmpRoot = root; + int tmpCount = count; + + root = other.root; + count = other.count; + + other.root = tmpRoot; + other.count = tmpCount; +} + +template +void asCMap::EraseAll() +{ + EraseAll(root); + root = 0; +} + +template +int asCMap::EraseAll(asSMapNode *p) +{ + if( p == 0 ) return -1; + + EraseAll( p->left ); + EraseAll( p->right ); + + typedef asSMapNode node_t; + asDELETE(p,node_t); + + count--; + + return 0; +} + +template +int asCMap::GetCount() const +{ + return count; +} + +template +int asCMap::Insert(const KEY &key, const VAL &value) +{ + typedef asSMapNode node_t; + asSMapNode *nnode = asNEW(node_t); + if( nnode == 0 ) + { + // Out of memory + return -1; + } + + nnode->key = key; + nnode->value = value; + + return Insert(nnode); +} + +template +int asCMap::Insert(asSMapNode *nnode) +{ + // Insert the node + if( root == 0 ) + root = nnode; + else + { + asSMapNode *p = root; + for(;;) + { + if( nnode->key < p->key ) + { + if( p->left == 0 ) + { + nnode->parent = p; + p->left = nnode; + break; + } + else + p = p->left; + } + else + { + if( p->right == 0 ) + { + nnode->parent = p; + p->right = nnode; + break; + } + else + p = p->right; + } + } + } + + BalanceInsert(nnode); + + count++; + + return 0; +} + +template +void asCMap::BalanceInsert(asSMapNode *node) +{ + // The node, that is red, can't have a red parent + while( node != root && node->parent->isRed ) + { + // Check color of uncle + if( node->parent == node->parent->parent->left ) + { + asSMapNode *uncle = node->parent->parent->right; + if( ISRED(uncle) ) + { + // B + // R R + // N + + // Change color on parent, uncle, and grand parent + node->parent->isRed = false; + uncle->isRed = false; + node->parent->parent->isRed = true; + + // Continue balancing from grand parent + node = node->parent->parent; + } + else + { + // B + // R B + // N + + if( node == node->parent->right ) + { + // Make the node a left child + node = node->parent; + RotateLeft(node); + } + + // Change color on parent and grand parent + // Then rotate grand parent to the right + node->parent->isRed = false; + node->parent->parent->isRed = true; + RotateRight(node->parent->parent); + } + } + else + { + asSMapNode *uncle = node->parent->parent->left; + if( ISRED(uncle) ) + { + // B + // R R + // N + + // Change color on parent, uncle, and grand parent + // Continue balancing from grand parent + node->parent->isRed = false; + uncle->isRed = false; + node = node->parent->parent; + node->isRed = true; + } + else + { + // B + // B R + // N + + if( node == node->parent->left ) + { + // Make the node a right child + node = node->parent; + RotateRight(node); + } + + // Change color on parent and grand parent + // Then rotate grand parent to the right + node->parent->isRed = false; + node->parent->parent->isRed = true; + RotateLeft(node->parent->parent); + } + } + } + + root->isRed = false; +} + +// For debugging purposes only +template +int asCMap::CheckIntegrity(asSMapNode *node) const +{ + if( node == 0 ) + { + if( root == 0 ) + return 0; + else if( ISRED(root) ) + return -1; + else + node = root; + } + + int left = 0, right = 0; + if( node->left ) + left = CheckIntegrity(node->left); + if( node->right ) + right = CheckIntegrity(node->right); + + if( left != right || left == -1 ) + return -1; + + if( ISBLACK(node) ) + return left+1; + + return left; +} + +// Returns true if successful +template +bool asCMap::MoveTo(asSMapNode **out, const KEY &key) const +{ + asSMapNode *p = root; + while( p ) + { + if( key < p->key ) + p = p->left; + else if( key == p->key ) + { + if( out ) *out = p; + return true; + } + else + p = p->right; + } + + if( out ) *out = 0; + return false; +} + +template +void asCMap::Erase(asSMapNode *cursor) +{ + asSMapNode *node = Remove(cursor); + asASSERT( node == cursor ); + + typedef asSMapNode node_t; + asDELETE(node,node_t); +} + +template +asSMapNode *asCMap::Remove(asSMapNode *cursor) +{ + if( cursor == 0 ) return 0; + + asSMapNode *node = cursor; + + //--------------------------------------------------- + // Choose the node that will replace the erased one + asSMapNode *remove; + if( node->left == 0 || node->right == 0 ) + remove = node; + else + { + remove = node->right; + while( remove->left ) remove = remove->left; + } + + //-------------------------------------------------- + // Remove the node + asSMapNode *child; + if( remove->left ) + child = remove->left; + else + child = remove->right; + + if( child ) child->parent = remove->parent; + if( remove->parent ) + { + if( remove == remove->parent->left ) + remove->parent->left = child; + else + remove->parent->right = child; + } + else + root = child; + + // If we remove a black node we must make sure the tree is balanced + if( ISBLACK(remove) ) + BalanceErase(child, remove->parent); + + //---------------------------------------- + // Replace the erased node with the removed one + if( remove != node ) + { + if( node->parent ) + { + if( node->parent->left == node ) + node->parent->left = remove; + else + node->parent->right = remove; + } + else + root = remove; + + remove->isRed = node->isRed; + remove->parent = node->parent; + + remove->left = node->left; + if( remove->left ) remove->left->parent = remove; + remove->right = node->right; + if( remove->right ) remove->right->parent = remove; + } + + count--; + + return node; +} + +// Call method only if removed node was black +// child is the child of the removed node +template +void asCMap::BalanceErase(asSMapNode *child, asSMapNode *parent) +{ + // If child is red + // Color child black + // Terminate + + // These tests assume brother is to the right. + + // 1. Brother is red + // Color parent red and brother black + // Rotate parent left + // Transforms to 2b + // 2a. Parent and brother is black, brother's children are black + // Color brother red + // Continue test with parent as child + // 2b. Parent is red, brother is black, brother's children are black + // Color parent black and brother red + // Terminate + // 3. Brother is black, and brother's left is red and brother's right is black + // Color brother red and brother's left black + // Rotate brother to right + // Transforms to 4. + // 4. Brother is black, brother's right is red + // Color brother's right black + // Color brother to color of parent + // Color parent black + // Rotate parent left + // Terminate + + while( child != root && ISBLACK(child) ) + { + if( child == parent->left ) + { + asSMapNode *brother = parent->right; + + // Case 1 + if( ISRED(brother) ) + { + brother->isRed = false; + parent->isRed = true; + RotateLeft(parent); + brother = parent->right; + } + + // Case 2 + if( brother == 0 ) break; + if( ISBLACK(brother->left) && ISBLACK(brother->right) ) + { + // Case 2b + if( ISRED(parent) ) + { + parent->isRed = false; + brother->isRed = true; + break; + } + + brother->isRed = true; + child = parent; + parent = child->parent; + } + else + { + // Case 3 + if( ISBLACK(brother->right) ) + { + brother->left->isRed = false; + brother->isRed = true; + RotateRight(brother); + brother = parent->right; + } + + // Case 4 + brother->isRed = parent->isRed; + parent->isRed = false; + brother->right->isRed = false; + RotateLeft(parent); + break; + } + } + else + { + asSMapNode *brother = parent->left; + + // Case 1 + if( ISRED(brother) ) + { + brother->isRed = false; + parent->isRed = true; + RotateRight(parent); + brother = parent->left; + } + + // Case 2 + if( brother == 0 ) break; + if( ISBLACK(brother->left) && ISBLACK(brother->right) ) + { + // Case 2b + if( ISRED(parent) ) + { + parent->isRed = false; + brother->isRed = true; + break; + } + + brother->isRed = true; + child = parent; + parent = child->parent; + } + else + { + // Case 3 + if( ISBLACK(brother->left) ) + { + brother->right->isRed = false; + brother->isRed = true; + RotateLeft(brother); + brother = parent->left; + } + + // Case 4 + brother->isRed = parent->isRed; + parent->isRed = false; + brother->left->isRed = false; + RotateRight(parent); + break; + } + } + } + + if( child ) + child->isRed = false; +} + +template +int asCMap::RotateRight(asSMapNode *node) +{ + // P L // + // / \ / \ // + // L R => Ll P // + // / \ / \ // + // Ll Lr Lr R // + + if( node->left == 0 ) return -1; + + asSMapNode *left = node->left; + + // Update parent + if( node->parent ) + { + asSMapNode *parent = node->parent; + if( parent->left == node ) + parent->left = left; + else + parent->right = left; + + left->parent = parent; + } + else + { + root = left; + left->parent = 0; + } + + // Move left's right child to node's left child + node->left = left->right; + if( node->left ) node->left->parent = node; + + // Put node as left's right child + left->right = node; + node->parent = left; + + return 0; +} + +template +int asCMap::RotateLeft(asSMapNode *node) +{ + // P R // + // / \ / \ // + // L R => P Rr // + // / \ / \ // + // Rl Rr L Rl // + + if( node->right == 0 ) return -1; + + asSMapNode *right = node->right; + + // Update parent + if( node->parent ) + { + asSMapNode *parent = node->parent; + if( parent->right == node ) + parent->right = right; + else + parent->left = right; + + right->parent = parent; + } + else + { + root = right; + right->parent = 0; + } + + // Move right's left child to node's right child + node->right = right->left; + if( node->right ) node->right->parent = node; + + // Put node as right's left child + right->left = node; + node->parent = right; + + return 0; +} + +template +const VAL &asCMap::GetValue(const asSMapNode *cursor) const +{ + if( cursor == 0 ) + return dummy.value; + + return cursor->value; +} + +template +VAL &asCMap::GetValue(asSMapNode *cursor) +{ + if( cursor == 0 ) + return dummy.value; + + return cursor->value; +} + +template +const KEY &asCMap::GetKey(const asSMapNode *cursor) const +{ + if( cursor == 0 ) + return dummy.key; + + return cursor->key; +} + +template +bool asCMap::MoveFirst(asSMapNode **out) const +{ + *out = root; + if( root == 0 ) return false; + + while( (*out)->left ) + *out = (*out)->left; + + return true; +} + +template +bool asCMap::MoveLast(asSMapNode **out) const +{ + *out = root; + if( root == 0 ) return false; + + while( (*out)->right ) + *out = (*out)->right; + + return true; +} + +template +bool asCMap::MoveNext(asSMapNode **out, asSMapNode *cursor) const +{ + if( cursor == 0 ) + { + *out = 0; + return false; + } + + if( cursor->right == 0 ) + { + // Move upwards until we find a parent node to the right + while( cursor->parent && cursor->parent->right == cursor ) + cursor = cursor->parent; + + cursor = cursor->parent; + *out = cursor; + if( cursor == 0 ) + return false; + + return true; + } + + cursor = cursor->right; + while( cursor->left ) + cursor = cursor->left; + + *out = cursor; + return true; +} + +template +bool asCMap::MovePrev(asSMapNode **out, asSMapNode *cursor) const +{ + if( cursor == 0 ) + { + *out = 0; + return false; + } + + if( cursor->left == 0 ) + { + // Move upwards until we find a parent node to the left + while( cursor->parent && cursor->parent->left == cursor ) + cursor = cursor->parent; + + cursor = cursor->parent; + + *out = cursor; + if( cursor == 0 ) + return false; + + return true; + } + + cursor = cursor->left; + while( cursor->right ) + cursor = cursor->right; + + *out = cursor; + return true; +} + + + + +#endif + diff --git a/3rdparty/angelscript/include/as_memory.h b/3rdparty/angelscript/include/as_memory.h new file mode 100644 index 0000000..369af64 --- /dev/null +++ b/3rdparty/angelscript/include/as_memory.h @@ -0,0 +1,135 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_memory.h +// +// Overload the default memory management functions so that we +// can let the application decide how to do it. +// + + + +#ifndef AS_MEMORY_H +#define AS_MEMORY_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +extern asALLOCFUNC_t userAlloc; +extern asFREEFUNC_t userFree; + +#ifdef WIP_16BYTE_ALIGN + +// TODO: This declaration should be in angelscript.h +// when the application can register it's own +// aligned memory routines +typedef void *(*asALLOCALIGNEDFUNC_t)(size_t, size_t); +typedef void (*asFREEALIGNEDFUNC_t)(void *); +extern asALLOCALIGNEDFUNC_t userAllocAligned; +extern asFREEALIGNEDFUNC_t userFreeAligned; +typedef void *(*asALLOCALIGNEDFUNCDEBUG_t)(size_t, size_t, const char *, unsigned int); + +// The maximum type alignment supported. +const int MAX_TYPE_ALIGNMENT = 16; + +// Utility function used for assertions. +bool isAligned(const void* const pointer, asUINT alignment); + +#endif // WIP_16BYTE_ALIGN + +// We don't overload the new operator as that would affect the application as well + +#ifndef AS_DEBUG + + #define asNEW(x) new(userAlloc(sizeof(x))) x + #define asDELETE(ptr,x) {void *tmp = ptr; (ptr)->~x(); userFree(tmp);} + + #define asNEWARRAY(x,cnt) (x*)userAlloc(sizeof(x)*cnt) + #define asDELETEARRAY(ptr) userFree(ptr) + +#ifdef WIP_16BYTE_ALIGN + #define asNEWARRAYALIGNED(x,cnt, alignment) (x*)userAllocAligned(sizeof(x)*cnt, alignment) + #define asDELETEARRAYALIGNED(ptr) userFreeAligned(ptr) +#endif + +#else + + typedef void *(*asALLOCFUNCDEBUG_t)(size_t, const char *, unsigned int); + + #define asNEW(x) new(((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(x), __FILE__, __LINE__)) x + #define asDELETE(ptr,x) {void *tmp = ptr; (ptr)->~x(); userFree(tmp);} + + #define asNEWARRAY(x,cnt) (x*)((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(x)*cnt, __FILE__, __LINE__) + #define asDELETEARRAY(ptr) userFree(ptr) + +#ifdef WIP_16BYTE_ALIGN + //TODO: Equivalent of debug allocation function with alignment? + #define asNEWARRAYALIGNED(x,cnt, alignment) (x*)userAllocAligned(sizeof(x)*cnt, alignment) + #define asDELETEARRAYALIGNED(ptr) userFreeAligned(ptr) +#endif + +#endif + +END_AS_NAMESPACE + +#include +#include "as_criticalsection.h" +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +class asCMemoryMgr +{ +public: + asCMemoryMgr(); + ~asCMemoryMgr(); + + void FreeUnusedMemory(); + + void *AllocScriptNode(); + void FreeScriptNode(void *ptr); + +#ifndef AS_NO_COMPILER + void *AllocByteInstruction(); + void FreeByteInstruction(void *ptr); +#endif + +protected: + DECLARECRITICALSECTION(cs) + asCArray scriptNodePool; + asCArray byteInstructionPool; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_module.h b/3rdparty/angelscript/include/as_module.h new file mode 100644 index 0000000..4d860e1 --- /dev/null +++ b/3rdparty/angelscript/include/as_module.h @@ -0,0 +1,246 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_module.h +// +// A class that holds a script module +// + +#ifndef AS_MODULE_H +#define AS_MODULE_H + +#include "as_config.h" +#include "as_symboltable.h" +#include "as_atomic.h" +#include "as_string.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_scriptfunction.h" +#include "as_property.h" + +BEGIN_AS_NAMESPACE + +// TODO: import: Remove this when the imported functions are removed +const int FUNC_IMPORTED = 0x40000000; + +class asCScriptEngine; +class asCCompiler; +class asCBuilder; +class asCContext; +class asCConfigGroup; +class asCTypedefType; +class asCFuncdefType; +struct asSNameSpace; + +struct sBindInfo +{ + asCScriptFunction *importedFunctionSignature; + asCString importFromModule; + int boundFunctionId; +}; + +struct sObjectTypePair +{ + asCObjectType *a; + asCObjectType *b; +}; + + +// TODO: import: Remove function imports. When I have implemented function +// pointers the function imports should be deprecated. + +// TODO: Need a separate interface for compiling scripts. The asIScriptCompiler +// will have a target module, and will allow the compilation of an entire +// script or just individual functions within the scope of the module +// +// With this separation it will be possible to compile the library without +// the compiler, thus giving a much smaller binary executable. + +// TODO: There should be a special compile option that will let the application +// recompile an already compiled script. The compiler should check if no +// destructive changes have been made (changing function signatures, etc) +// then it should simply replace the bytecode within the functions without +// changing the values of existing global properties, etc. + +class asCModule : public asIScriptModule +{ +//------------------------------------------- +// Public interface +//-------------------------------------------- +public: + virtual asIScriptEngine *GetEngine() const; + virtual void SetName(const char *name); + virtual const char *GetName() const; + virtual void Discard(); + + // Compilation + virtual int AddScriptSection(const char *name, const char *code, size_t codeLength, int lineOffset); + virtual int Build(); + virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD reserved, asIScriptFunction **outFunc); + virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset); + virtual asDWORD SetAccessMask(asDWORD accessMask); + virtual int SetDefaultNamespace(const char *nameSpace); + virtual const char *GetDefaultNamespace() const; + + // Script functions + virtual asUINT GetFunctionCount() const; + virtual asIScriptFunction *GetFunctionByIndex(asUINT index) const; + virtual asIScriptFunction *GetFunctionByDecl(const char *decl) const; + virtual asIScriptFunction *GetFunctionByName(const char *name) const; + virtual int RemoveFunction(asIScriptFunction *func); + + // Script global variables + // TODO: interface: Should be called InitGlobalVars, and should have a bool to reset in case already initialized + virtual int ResetGlobalVars(asIScriptContext *ctx); + virtual asUINT GetGlobalVarCount() const; + virtual int GetGlobalVarIndexByName(const char *name) const; + virtual int GetGlobalVarIndexByDecl(const char *decl) const; + virtual const char *GetGlobalVarDeclaration(asUINT index, bool includeNamespace) const; + virtual int GetGlobalVar(asUINT index, const char **name, const char **nameSpace, int *typeId, bool *isConst) const; + virtual void *GetAddressOfGlobalVar(asUINT index); + virtual int RemoveGlobalVar(asUINT index); + + // Type identification + virtual asUINT GetObjectTypeCount() const; + virtual asITypeInfo *GetObjectTypeByIndex(asUINT index) const; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeByName(const char *name) const; + virtual asITypeInfo *GetObjectTypeByDecl(const char *decl) const; +#endif + virtual int GetTypeIdByDecl(const char *decl) const; + virtual asITypeInfo *GetTypeInfoByName(const char *name) const; + virtual asITypeInfo *GetTypeInfoByDecl(const char *decl) const; + + // Enums + virtual asUINT GetEnumCount() const; + virtual asITypeInfo *GetEnumByIndex(asUINT index) const; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual int GetEnumValueCount(int enumTypeId) const; + virtual const char * GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const; +#endif + + // Typedefs + virtual asUINT GetTypedefCount() const; + virtual asITypeInfo *GetTypedefByIndex(asUINT index) const; + + // Dynamic binding between modules + virtual asUINT GetImportedFunctionCount() const; + virtual int GetImportedFunctionIndexByDecl(const char *decl) const; + virtual const char *GetImportedFunctionDeclaration(asUINT importIndex) const; + virtual const char *GetImportedFunctionSourceModule(asUINT importIndex) const; + virtual int BindImportedFunction(asUINT index, asIScriptFunction *func); + virtual int UnbindImportedFunction(asUINT importIndex); + virtual int BindAllImportedFunctions(); + virtual int UnbindAllImportedFunctions(); + + // Bytecode Saving/Loading + virtual int SaveByteCode(asIBinaryStream *out, bool stripDebugInfo) const; + virtual int LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped); + + // User data + virtual void *SetUserData(void *data, asPWORD type); + virtual void *GetUserData(asPWORD type) const; + +//----------------------------------------------- +// Internal +//----------------------------------------------- + asCModule(const char *name, asCScriptEngine *engine); + ~asCModule(); + +//protected: + friend class asCScriptEngine; + friend class asCBuilder; + friend class asCCompiler; + friend class asCContext; + friend class asCRestore; + + void InternalReset(); + bool IsEmpty() const; + bool HasExternalReferences(bool shuttingDown); + + int CallInit(asIScriptContext *ctx); + void CallExit(); + + void JITCompile(); + +#ifndef AS_NO_COMPILER + int AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray ¶mNames, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType = 0, bool isConstMethod = false, bool isGlobalFunction = false, bool isPrivate = false, bool isProtected = false, bool isFinal = false, bool isOverride = false, bool isShared = false, asSNameSpace *ns = 0); + int AddScriptFunction(asCScriptFunction *func); + int AddImportedFunction(int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray &inOutFlags, const asCArray &defaultArgs, asSNameSpace *ns, const asCString &moduleName); + int AddFuncDef(const asCString &name, asSNameSpace *ns, asCObjectType *parent); +#endif + + int GetNextImportedFunctionId(); + asCScriptFunction *GetImportedFunction(int funcId) const; + asCTypeInfo *GetType(const char *type, asSNameSpace *ns); + asCObjectType *GetObjectType(const char *type, asSNameSpace *ns); + asCGlobalProperty *AllocateGlobalProperty(const char *name, const asCDataType &dt, asSNameSpace *ns); + + asCString name; + + asCScriptEngine *engine; + asCBuilder *builder; + asCArray userData; + asDWORD accessMask; + asSNameSpace *defaultNamespace; + + // This array holds all functions, class members, factories, etc that were compiled with the module. + // These references hold an internal reference to the function object. + asCArray scriptFunctions; // increases ref count + // This array holds global functions declared in the module. These references are not counted, + // as the same pointer is always present in the scriptFunctions array too. + asCSymbolTable globalFunctions; // doesn't increase ref count + // This array holds imported functions in the module. + asCArray bindInformations; // increases ref count + // This array holds template instance types created for the module's object types + asCArray templateInstances; // increases ref count + + // This array holds the global variables declared in the script + asCSymbolTable scriptGlobals; // increases ref count + bool isGlobalVarInitialized; + + // This array holds class and interface types + asCArray classTypes; // increases ref count + // This array holds enum types + asCArray enumTypes; // increases ref count + // This array holds typedefs + asCArray typeDefs; // increases ref count + // This array holds the funcdefs declared in the module + asCArray funcDefs; // increases ref count +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_namespace.h b/3rdparty/angelscript/include/as_namespace.h new file mode 100644 index 0000000..ae384e7 --- /dev/null +++ b/3rdparty/angelscript/include/as_namespace.h @@ -0,0 +1,77 @@ +/* + AngelCode Scripting Library + Copyright (c) 2013-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#ifndef AS_NAMESPACE_H +#define AS_NAMESPACE_H + +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +struct asSNameSpace +{ + asCString name; + + // TODO: namespace: A namespace should have access masks. The application should be + // able to restrict specific namespaces from access to specific modules +}; + + +struct asSNameSpaceNamePair +{ + const asSNameSpace *ns; + asCString name; + + asSNameSpaceNamePair() : ns(0) {} + asSNameSpaceNamePair(const asSNameSpace *_ns, const asCString &_name) : ns(_ns), name(_name) {} + + asSNameSpaceNamePair &operator=(const asSNameSpaceNamePair &other) + { + ns = other.ns; + name = other.name; + return *this; + } + + bool operator==(const asSNameSpaceNamePair &other) const + { + return (ns == other.ns && name == other.name); + } + + bool operator<(const asSNameSpaceNamePair &other) const + { + return (ns < other.ns || (ns == other.ns && name < other.name)); + } +}; + +END_AS_NAMESPACE + +#endif + diff --git a/3rdparty/angelscript/include/as_objecttype.h b/3rdparty/angelscript/include/as_objecttype.h new file mode 100644 index 0000000..87f7c54 --- /dev/null +++ b/3rdparty/angelscript/include/as_objecttype.h @@ -0,0 +1,171 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_objecttype.h +// +// A class for storing object type information +// + + + +#ifndef AS_OBJECTTYPE_H +#define AS_OBJECTTYPE_H + +#include "as_property.h" +#include "as_array.h" +#include "as_scriptfunction.h" +#include "as_typeinfo.h" + +BEGIN_AS_NAMESPACE + +struct asSTypeBehaviour +{ + asSTypeBehaviour() + { + factory = 0; + listFactory = 0; + copyfactory = 0; + construct = 0; + copyconstruct = 0; + destruct = 0; + copy = 0; + addref = 0; + release = 0; + gcGetRefCount = 0; + gcSetFlag = 0; + gcGetFlag = 0; + gcEnumReferences = 0; + gcReleaseAllReferences = 0; + templateCallback = 0; + getWeakRefFlag = 0; + } + + int factory; + int listFactory; // Used for initialization lists only + int copyfactory; + int construct; + int copyconstruct; + int destruct; + int copy; + int addref; + int release; + int templateCallback; + + // GC behaviours + int gcGetRefCount; + int gcSetFlag; + int gcGetFlag; + int gcEnumReferences; + int gcReleaseAllReferences; + + // Weakref behaviours + int getWeakRefFlag; + + asCArray factories; + asCArray constructors; +}; + +class asCScriptEngine; +struct asSNameSpace; + +class asCObjectType : public asCTypeInfo +{ +public: + asITypeInfo *GetBaseType() const; + bool DerivesFrom(const asITypeInfo *objType) const; + int GetSubTypeId(asUINT subtypeIndex = 0) const; + asITypeInfo *GetSubType(asUINT subtypeIndex = 0) const; + asUINT GetSubTypeCount() const; + asUINT GetInterfaceCount() const; + asITypeInfo *GetInterface(asUINT index) const; + bool Implements(const asITypeInfo *objType) const; + asUINT GetFactoryCount() const; + asIScriptFunction *GetFactoryByIndex(asUINT index) const; + asIScriptFunction *GetFactoryByDecl(const char *decl) const; + asUINT GetMethodCount() const; + asIScriptFunction *GetMethodByIndex(asUINT index, bool getVirtual) const; + asIScriptFunction *GetMethodByName(const char *name, bool getVirtual) const; + asIScriptFunction *GetMethodByDecl(const char *decl, bool getVirtual) const; + asUINT GetPropertyCount() const; + int GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, bool *isProtected, int *offset, bool *isReference, asDWORD *accessMask) const; + const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const; + asUINT GetBehaviourCount() const; + asIScriptFunction *GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const; + asUINT GetChildFuncdefCount() const; + asITypeInfo *GetChildFuncdef(asUINT index) const; + +public: + asCObjectType(asCScriptEngine *engine); + ~asCObjectType(); + void DestroyInternal(); + + void ReleaseAllFunctions(); + + bool IsInterface() const; + + asCObjectProperty *AddPropertyToClass(const asCString &name, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited); + void ReleaseAllProperties(); + +#ifdef WIP_16BYTE_ALIGN + int alignment; +#endif + asCArray properties; + asCArray methods; + + // TODO: These are not used by template types. Should perhaps create a derived class to save memory on ordinary object types + asCArray interfaces; + asCArray interfaceVFTOffsets; + asCObjectType * derivedFrom; + asCArray virtualFunctionTable; + + // Used for funcdefs declared as members of class. + // TODO: child funcdef: Should be possible to enumerate these from application + asCArray childFuncDefs; + + asSTypeBehaviour beh; + + // Used for template types + asCArray templateSubTypes; // increases refCount for typeinfo held in datatype + bool acceptValueSubType; + bool acceptRefSubType; + +protected: + friend class asCScriptEngine; + friend class asCConfigGroup; + friend class asCModule; + asCObjectType(); +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_outputbuffer.h b/3rdparty/angelscript/include/as_outputbuffer.h new file mode 100644 index 0000000..500b162 --- /dev/null +++ b/3rdparty/angelscript/include/as_outputbuffer.h @@ -0,0 +1,80 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2012 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_outputbuffer.h +// +// This class appends strings to one large buffer that can later +// be sent to the real output stream +// + + +#ifndef AS_OUTPUTBUFFER_H +#define AS_OUTPUTBUFFER_H + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_string.h" +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +struct asSSystemFunctionInterface; +class asCScriptEngine; + +class asCOutputBuffer +{ +public: + ~asCOutputBuffer (); + void Clear(); + void Callback(asSMessageInfo *msg); + void Append(asCOutputBuffer &in); + void SendToCallback(asCScriptEngine *engine, asSSystemFunctionInterface *func, void *obj); + + struct message_t + { + asCString section; + int row; + int col; + asEMsgType type; + asCString msg; + }; + + asCArray messages; +}; + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + +#endif diff --git a/3rdparty/angelscript/include/as_parser.h b/3rdparty/angelscript/include/as_parser.h new file mode 100644 index 0000000..8ace007 --- /dev/null +++ b/3rdparty/angelscript/include/as_parser.h @@ -0,0 +1,195 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_parser.h +// +// This class parses the script code and builds a tree for compilation +// + + + +#ifndef AS_PARSER_H +#define AS_PARSER_H + +#include "as_scriptnode.h" +#include "as_scriptcode.h" +#include "as_builder.h" +#include "as_tokenizer.h" + +BEGIN_AS_NAMESPACE + +class asCParser +{ +public: + asCParser(asCBuilder *builder); + ~asCParser(); + + int ParseFunctionDefinition(asCScriptCode *script, bool expectListPattern); + int ParsePropertyDeclaration(asCScriptCode *script); + int ParseDataType(asCScriptCode *script, bool isReturnType); + int ParseTemplateDecl(asCScriptCode *script); + +#ifndef AS_NO_COMPILER + int ParseScript(asCScriptCode *script); + + // Called from compiler + int ParseStatementBlock(asCScriptCode *script, asCScriptNode *block); + int ParseVarInit(asCScriptCode *script, asCScriptNode *init); + int ParseExpression(asCScriptCode *script); +#endif + + asCScriptNode *GetScriptNode(); + +protected: + void Reset(); + + void GetToken(sToken *token); + void RewindTo(const sToken *token); + void SetPos(size_t pos); + void Error(const asCString &text, sToken *token); + void Warning(const asCString &text, sToken *token); + void Info(const asCString &text, sToken *token); + + asCScriptNode *CreateNode(eScriptNode type); + + asCScriptNode *ParseFunctionDefinition(); + asCScriptNode *ParseParameterList(); + asCScriptNode *SuperficiallyParseExpression(); + asCScriptNode *ParseType(bool allowConst, bool allowVariableType = false, bool allowAuto = false); + asCScriptNode *ParseTypeMod(bool isParam); + void ParseOptionalScope(asCScriptNode *node); + asCScriptNode *ParseRealType(); + asCScriptNode *ParseDataType(bool allowVariableType = false, bool allowAuto = false); + asCScriptNode *ParseIdentifier(); + bool ParseTemplTypeList(asCScriptNode *node, bool required = true); + + asCScriptNode *ParseListPattern(); + + bool IsRealType(int tokenType); + bool IsDataType(const sToken &token); + bool IdentifierIs(const sToken &t, const char *str); + +#ifndef AS_NO_COMPILER + // Statements + asCScriptNode *SuperficiallyParseStatementBlock(); + asCScriptNode *SuperficiallyParseVarInit(); + asCScriptNode *ParseStatementBlock(); + asCScriptNode *ParseStatement(); + asCScriptNode *ParseExpressionStatement(); + asCScriptNode *ParseSwitch(); + asCScriptNode *ParseCase(); + asCScriptNode *ParseIf(); + asCScriptNode *ParseFor(); + asCScriptNode *ParseWhile(); + asCScriptNode *ParseDoWhile(); + asCScriptNode *ParseReturn(); + asCScriptNode *ParseBreak(); + asCScriptNode *ParseContinue(); + + // Declarations + asCScriptNode *ParseDeclaration(bool isClassProp = false, bool isGlobalVar = false); + asCScriptNode *ParseImport(); + asCScriptNode *ParseScript(bool inBlock); + asCScriptNode *ParseNamespace(); + asCScriptNode *ParseFunction(bool isMethod = false); + asCScriptNode *ParseFuncDef(); + asCScriptNode *ParseClass(); + asCScriptNode *ParseMixin(); + asCScriptNode *ParseInitList(); + asCScriptNode *ParseInterface(); + asCScriptNode *ParseInterfaceMethod(); + asCScriptNode *ParseVirtualPropertyDecl(bool isMethod, bool isInterface); + asCScriptNode *ParseEnumeration(); + asCScriptNode *ParseTypedef(); + void ParseMethodOverrideBehaviors(asCScriptNode *funcNode); + bool IsVarDecl(); + bool IsVirtualPropertyDecl(); + bool IsFuncDecl(bool isMethod); + bool IsLambda(); + bool IsFunctionCall(); + + // Expressions + asCScriptNode *ParseAssignment(); + asCScriptNode *ParseAssignOperator(); + asCScriptNode *ParseCondition(); + asCScriptNode *ParseExpression(); + asCScriptNode *ParseExprTerm(); + asCScriptNode *ParseExprOperator(); + asCScriptNode *ParseExprPreOp(); + asCScriptNode *ParseExprPostOp(); + asCScriptNode *ParseExprValue(); + asCScriptNode *ParseArgList(bool withParenthesis = true); + asCScriptNode *ParseFunctionCall(); + asCScriptNode *ParseVariableAccess(); + asCScriptNode *ParseConstructCall(); + asCScriptNode *ParseCast(); + asCScriptNode *ParseConstant(); + asCScriptNode *ParseStringConstant(); + asCScriptNode *ParseLambda(); + + bool IsConstant(int tokenType); + bool IsOperator(int tokenType); + bool IsPreOperator(int tokenType); + bool IsPostOperator(int tokenType); + bool IsAssignOperator(int tokenType); + + bool CheckTemplateType(const sToken &t); +#endif + + asCScriptNode *ParseToken(int token); + asCScriptNode *ParseOneOf(int *tokens, int num); + + asCString ExpectedToken(const char *token); + asCString ExpectedTokens(const char *token1, const char *token2); + asCString ExpectedOneOf(int *tokens, int count); + asCString ExpectedOneOf(const char **tokens, int count); + asCString InsteadFound(sToken &t); + + bool errorWhileParsing; + bool isSyntaxError; + bool checkValidTypes; + bool isParsingAppInterface; + + asCScriptEngine *engine; + asCBuilder *builder; + asCScriptCode *script; + asCScriptNode *scriptNode; + + asCString tempString; // Used for reduzing amount of dynamic allocations + + sToken lastToken; + size_t sourcePos; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_property.h b/3rdparty/angelscript/include/as_property.h new file mode 100644 index 0000000..983ee64 --- /dev/null +++ b/3rdparty/angelscript/include/as_property.h @@ -0,0 +1,127 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_property.h +// +// A class for storing object property information +// + + + +#ifndef AS_PROPERTY_H +#define AS_PROPERTY_H + +#include "as_string.h" +#include "as_datatype.h" +#include "as_atomic.h" +#include "as_scriptfunction.h" +#include "as_symboltable.h" + +BEGIN_AS_NAMESPACE + +struct asSNameSpace; + +class asCObjectProperty +{ +public: + asCObjectProperty() {accessMask = 0xFFFFFFFF;} + asCObjectProperty(const asCObjectProperty &o) : name(o.name), type(o.type), byteOffset(o.byteOffset), accessMask(o.accessMask), isPrivate(o.isPrivate), isProtected(o.isProtected), isInherited(o.isInherited) {} + asCString name; + asCDataType type; + int byteOffset; + asDWORD accessMask; + bool isPrivate; + bool isProtected; + bool isInherited; +}; + +class asCGlobalProperty +{ +public: + asCGlobalProperty(); + ~asCGlobalProperty(); + + void AddRef(); + void Release(); + void DestroyInternal(); + + void *GetAddressOfValue(); + void AllocateMemory(); + void SetRegisteredAddress(void *p); + void *GetRegisteredAddress() const; + + asCString name; + asCDataType type; + asUINT id; + asSNameSpace *nameSpace; + + void SetInitFunc(asCScriptFunction *initFunc); + asCScriptFunction *GetInitFunc(); + +//protected: + // This is only stored for registered properties, and keeps the pointer given by the application + void *realAddress; + + bool memoryAllocated; + void *memory; + asQWORD storage; + + asCScriptFunction *initFunc; + + asDWORD accessMask; + + // The global property structure is reference counted, so that the + // engine can keep track of how many references to the property there are. + asCAtomic refCount; +}; + +class asCCompGlobPropType : public asIFilter +{ +public: + const asCDataType &m_type; + + asCCompGlobPropType(const asCDataType &type) : m_type(type) {} + + bool operator()(const void *p) const + { + const asCGlobalProperty* prop = reinterpret_cast(p); + return prop->type == m_type; + } + +private: + // The assignment operator is required for MSVC9, otherwise it will complain that it is not possible to auto generate the operator + asCCompGlobPropType &operator=(const asCCompGlobPropType &) {return *this;} +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_restore.h b/3rdparty/angelscript/include/as_restore.h new file mode 100644 index 0000000..1c549aa --- /dev/null +++ b/3rdparty/angelscript/include/as_restore.h @@ -0,0 +1,255 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_restore.h +// +// Functions for saving and restoring module bytecode +// asCRestore was originally written by Dennis Bollyn, dennis@gyrbo.be +// It was later split in two classes asCReader and asCWriter by me + +#ifndef AS_RESTORE_H +#define AS_RESTORE_H + +#include "as_scriptengine.h" +#include "as_context.h" +#include "as_map.h" + +BEGIN_AS_NAMESPACE + +class asCReader +{ +public: + asCReader(asCModule *module, asIBinaryStream *stream, asCScriptEngine *engine); + + int Read(bool *wasDebugInfoStripped); + +protected: + asCModule *module; + asIBinaryStream *stream; + asCScriptEngine *engine; + bool noDebugInfo; + bool error; + asUINT bytesRead; + + int Error(const char *msg); + + int ReadInner(); + + void ReadData(void *data, asUINT size); + void ReadString(asCString *str); + asCScriptFunction *ReadFunction(bool &isNew, bool addToModule = true, bool addToEngine = true, bool addToGC = true); + void ReadFunctionSignature(asCScriptFunction *func, asCObjectType **parentClass = 0); + void ReadGlobalProperty(); + void ReadObjectProperty(asCObjectType *ot); + void ReadDataType(asCDataType *dt); + asCTypeInfo *ReadTypeInfo(); + void ReadTypeDeclaration(asCTypeInfo *ot, int phase); + void ReadByteCode(asCScriptFunction *func); + asWORD ReadEncodedUInt16(); + asUINT ReadEncodedUInt(); + asQWORD ReadEncodedUInt64(); + + void ReadUsedTypeIds(); + void ReadUsedFunctions(); + void ReadUsedGlobalProps(); + void ReadUsedStringConstants(); + void ReadUsedObjectProps(); + + asCTypeInfo * FindType(int idx); + int FindTypeId(int idx); + short FindObjectPropOffset(asWORD index); + asCScriptFunction *FindFunction(int idx); + + // After loading, each function needs to be translated to update pointers, function ids, etc + void TranslateFunction(asCScriptFunction *func); + void CalculateAdjustmentByPos(asCScriptFunction *func); + int AdjustStackPosition(int pos); + int AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos); + void CalculateStackNeeded(asCScriptFunction *func); + asCScriptFunction *GetCalledFunction(asCScriptFunction *func, asDWORD programPos); + + // Temporary storage for persisting variable data + asCArray usedTypeIds; + asCArray usedTypes; + asCArray usedFunctions; + asCArray usedGlobalProperties; + asCArray usedStringConstants; + + asCArray savedFunctions; + asCArray savedDataTypes; + asCArray savedStrings; + + asCArray adjustByPos; + asCArray adjustNegativeStackByPos; + + struct SObjProp + { + asCObjectType *objType; + int offset; + }; + asCArray usedObjectProperties; + + asCMap existingShared; + asCMap dontTranslate; + + // Helper class for adjusting offsets within initialization list buffers + struct SListAdjuster + { + SListAdjuster(asCReader *rd, asDWORD *bc, asCObjectType *ot); + void AdjustAllocMem(); + int AdjustOffset(int offset); + void SetRepeatCount(asUINT rc); + void SetNextType(int typeId); + + struct SInfo + { + asUINT repeatCount; + asSListPatternNode *startNode; + }; + asCArray stack; + + asCReader *reader; + asDWORD *allocMemBC; + asUINT maxOffset; + asCObjectType *patternType; + asUINT repeatCount; + int lastOffset; + int nextOffset; + asUINT lastAdjustedOffset; + asSListPatternNode *patternNode; + int nextTypeId; + }; + asCArray listAdjusters; +}; + +#ifndef AS_NO_COMPILER + +class asCWriter +{ +public: + asCWriter(asCModule *module, asIBinaryStream *stream, asCScriptEngine *engine, bool stripDebugInfo); + + int Write(); + +protected: + asCModule *module; + asIBinaryStream *stream; + asCScriptEngine *engine; + bool stripDebugInfo; + + void WriteData(const void *data, asUINT size); + + void WriteString(asCString *str); + void WriteFunction(asCScriptFunction *func); + void WriteFunctionSignature(asCScriptFunction *func); + void WriteGlobalProperty(asCGlobalProperty *prop); + void WriteObjectProperty(asCObjectProperty *prop); + void WriteDataType(const asCDataType *dt); + void WriteTypeInfo(asCTypeInfo *ot); + void WriteTypeDeclaration(asCTypeInfo *ot, int phase); + void WriteByteCode(asCScriptFunction *func); + void WriteEncodedInt64(asINT64 i); + + // Helper functions for storing variable data + int FindTypeInfoIdx(asCTypeInfo *ti); + int FindTypeIdIdx(int typeId); + int FindFunctionIndex(asCScriptFunction *func); + int FindGlobalPropPtrIndex(void *); + int FindStringConstantIndex(int id); + int FindObjectPropIndex(short offset, int typeId); + + void CalculateAdjustmentByPos(asCScriptFunction *func); + int AdjustStackPosition(int pos); + int AdjustProgramPosition(int pos); + int AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos); + + // Intermediate data used for storing that which isn't constant, function id's, pointers, etc + void WriteUsedTypeIds(); + void WriteUsedFunctions(); + void WriteUsedGlobalProps(); + void WriteUsedStringConstants(); + void WriteUsedObjectProps(); + + // Temporary storage for persisting variable data + asCArray usedTypeIds; + asCArray usedTypes; + asCArray usedFunctions; + asCArray usedGlobalProperties; + asCArray usedStringConstants; + asCMap stringIdToIndexMap; + + asCArray savedFunctions; + asCArray savedDataTypes; + asCArray savedStrings; + asCMap stringToIdMap; + asCArray adjustStackByPos; + asCArray adjustNegativeStackByPos; + asCArray bytecodeNbrByPos; + + struct SObjProp + { + asCObjectType *objType; + int offset; + }; + asCArray usedObjectProperties; + + // Helper class for adjusting offsets within initialization list buffers + struct SListAdjuster + { + SListAdjuster(asCObjectType *ot); + int AdjustOffset(int offset, asCObjectType *listPatternType); + void SetRepeatCount(asUINT rc); + void SetNextType(int typeId); + + struct SInfo + { + asUINT repeatCount; + asSListPatternNode *startNode; + }; + asCArray stack; + + asCObjectType *patternType; + asUINT repeatCount; + asSListPatternNode *patternNode; + asUINT entries; + int lastOffset; // Last offset adjusted + int nextOffset; // next expected offset to be adjusted + int nextTypeId; + }; + asCArray listAdjusters; +}; + +#endif + +END_AS_NAMESPACE + +#endif // AS_RESTORE_H diff --git a/3rdparty/angelscript/include/as_scriptcode.h b/3rdparty/angelscript/include/as_scriptcode.h new file mode 100644 index 0000000..be1af3b --- /dev/null +++ b/3rdparty/angelscript/include/as_scriptcode.h @@ -0,0 +1,72 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2011 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptcode.h +// +// A container class for the script code to be compiled +// + + + +#ifndef AS_SCRIPTCODE_H +#define AS_SCRIPTCODE_H + +#include "as_array.h" +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +class asCScriptCode +{ +public: + asCScriptCode(); + ~asCScriptCode(); + + int SetCode(const char *name, const char *code, bool makeCopy); + int SetCode(const char *name, const char *code, size_t length, bool makeCopy); + + void ConvertPosToRowCol(size_t pos, int *row, int *col); + + bool TokenEquals(size_t pos, size_t len, const char *str); + + asCString name; + char *code; + size_t codeLength; + bool sharedCode; + int idx; + int lineOffset; + asCArray linePositions; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_scriptengine.h b/3rdparty/angelscript/include/as_scriptengine.h new file mode 100644 index 0000000..10aa328 --- /dev/null +++ b/3rdparty/angelscript/include/as_scriptengine.h @@ -0,0 +1,545 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptengine.h +// +// The implementation of the script engine interface +// + + + +#ifndef AS_SCRIPTENGINE_H +#define AS_SCRIPTENGINE_H + +#include "as_config.h" +#include "as_atomic.h" +#include "as_scriptfunction.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_objecttype.h" +#include "as_module.h" +#include "as_callfunc.h" +#include "as_configgroup.h" +#include "as_memory.h" +#include "as_gc.h" +#include "as_tokenizer.h" + +BEGIN_AS_NAMESPACE + +class asCBuilder; +class asCContext; + +// TODO: import: Remove this when import is removed +struct sBindInfo; + +class asCScriptEngine : public asIScriptEngine +{ +//============================================================= +// From asIScriptEngine +//============================================================= +public: + // Memory management + virtual int AddRef() const; + virtual int Release() const; + virtual int ShutDownAndRelease(); + + // Engine properties + virtual int SetEngineProperty(asEEngineProp property, asPWORD value); + virtual asPWORD GetEngineProperty(asEEngineProp property) const; + + // Compiler messages + virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv); + virtual int ClearMessageCallback(); + virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message); + + // JIT Compiler + virtual int SetJITCompiler(asIJITCompiler *compiler); + virtual asIJITCompiler *GetJITCompiler() const; + + // Global functions + virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0); + virtual asUINT GetGlobalFunctionCount() const; + virtual asIScriptFunction *GetGlobalFunctionByIndex(asUINT index) const; + virtual asIScriptFunction *GetGlobalFunctionByDecl(const char *declaration) const; + + // Global properties + virtual int RegisterGlobalProperty(const char *declaration, void *pointer); + virtual asUINT GetGlobalPropertyCount() const; + virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0, asDWORD *accessMask = 0) const; + virtual int GetGlobalPropertyIndexByName(const char *name) const; + virtual int GetGlobalPropertyIndexByDecl(const char *decl) const; + + // Type registration + virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags); + virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset); + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0); + virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0); + virtual int RegisterInterface(const char *name); + virtual int RegisterInterfaceMethod(const char *intf, const char *declaration); + virtual asUINT GetObjectTypeCount() const; + virtual asITypeInfo *GetObjectTypeByIndex(asUINT index) const; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeByName(const char *name) const; + virtual asITypeInfo *GetObjectTypeByDecl(const char *decl) const; +#endif + + // String factory + virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv, void *auxiliary = 0); + virtual int GetStringFactoryReturnTypeId(asDWORD *flags) const; + + // Default array type + virtual int RegisterDefaultArrayType(const char *type); + virtual int GetDefaultArrayTypeId() const; + + // Enums + virtual int RegisterEnum(const char *type); + virtual int RegisterEnumValue(const char *type, const char *name, int value); + virtual asUINT GetEnumCount() const; + virtual asITypeInfo *GetEnumByIndex(asUINT index) const; +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual int GetEnumValueCount(int enumTypeId) const; + virtual const char * GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const; +#endif + + // Funcdefs + virtual int RegisterFuncdef(const char *decl); + virtual asUINT GetFuncdefCount() const; + virtual asITypeInfo *GetFuncdefByIndex(asUINT index) const; + + // Typedefs + // TODO: interface: Should perhaps rename this to Alias, since it doesn't really create a new type + virtual int RegisterTypedef(const char *type, const char *decl); + virtual asUINT GetTypedefCount() const; + virtual asITypeInfo *GetTypedefByIndex(asUINT index) const; + + // Configuration groups + virtual int BeginConfigGroup(const char *groupName); + virtual int EndConfigGroup(); + virtual int RemoveConfigGroup(const char *groupName); + virtual asDWORD SetDefaultAccessMask(asDWORD defaultMask); + virtual int SetDefaultNamespace(const char *nameSpace); + virtual const char *GetDefaultNamespace() const; + + // Script modules + virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag); + virtual int DiscardModule(const char *module); + virtual asUINT GetModuleCount() const; + virtual asIScriptModule *GetModuleByIndex(asUINT index) const; + + // Script functions + virtual asIScriptFunction *GetFunctionById(int funcId) const; +#ifdef AS_DEPRECATED + // deprecated since 2.31.0, 2016-01-01 + virtual asIScriptFunction *GetFuncdefFromTypeId(int typeId) const; +#endif + + // Type identification +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual asITypeInfo *GetObjectTypeById(int typeId) const; +#endif + virtual int GetTypeIdByDecl(const char *decl) const; + virtual const char *GetTypeDeclaration(int typeId, bool includeNamespace = false) const; + virtual int GetSizeOfPrimitiveType(int typeId) const; + virtual asITypeInfo *GetTypeInfoById(int typeId) const; + virtual asITypeInfo *GetTypeInfoByName(const char *name) const; + virtual asITypeInfo *GetTypeInfoByDecl(const char *decl) const; + + // Script execution + virtual asIScriptContext *CreateContext(); + virtual void *CreateScriptObject(const asITypeInfo *type); + virtual void *CreateScriptObjectCopy(void *obj, const asITypeInfo *type); + virtual void *CreateUninitializedScriptObject(const asITypeInfo *type); + virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj); + virtual int AssignScriptObject(void *dstObj, void *srcObj, const asITypeInfo *type); + virtual void ReleaseScriptObject(void *obj, const asITypeInfo *type); + virtual void AddRefScriptObject(void *obj, const asITypeInfo *type); + virtual int RefCastObject(void *obj, asITypeInfo *fromType, asITypeInfo *toType, void **newPtr, bool useOnlyImplicitCast = false); +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0, 2014-11-04 + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const; +#endif + virtual asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asITypeInfo *type) const; + + // Context pooling + virtual asIScriptContext *RequestContext(); + virtual void ReturnContext(asIScriptContext *ctx); + virtual int SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param = 0); + + // String interpretation + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, asUINT *tokenLength = 0) const; + + // Garbage collection + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE, asUINT numIterations = 1); + virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const; + virtual int NotifyGarbageCollectorOfNewObject(void *obj, asITypeInfo *type); + virtual int GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj = 0, asITypeInfo **type = 0); + virtual void GCEnumCallback(void *reference); + + // User data + virtual void *SetUserData(void *data, asPWORD type); + virtual void *GetUserData(asPWORD type) const; + virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type); + virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type); + virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type); + virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type); +#ifdef AS_DEPRECATED + // Deprecated since 2.31.0, 2015-12-06 + virtual void SetObjectTypeUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type); +#endif + virtual void SetTypeInfoUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type); + virtual void SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type); + +//=========================================================== +// internal methods +//=========================================================== +public: + asCScriptEngine(); + virtual ~asCScriptEngine(); + +//protected: + friend class asCBuilder; + friend class asCCompiler; + friend class asCContext; + friend class asCDataType; + friend class asCModule; + friend class asCRestore; + friend class asCByteCode; + friend int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + + int RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0); + int RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary = 0); + + int VerifyVarTypeNotInFunction(asCScriptFunction *func); + + void *CallAlloc(const asCObjectType *objType) const; + void CallFree(void *obj) const; + + void *CallGlobalFunctionRetPtr(int func) const; + void *CallGlobalFunctionRetPtr(int func, void *param1) const; + void *CallGlobalFunctionRetPtr(asSSystemFunctionInterface *func, asCScriptFunction *desc) const; + void *CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s, void *param1) const; + void CallObjectMethod(void *obj, int func) const; + void CallObjectMethod(void *obj, void *param, int func) const; + void CallObjectMethod(void *obj, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; + void CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; + bool CallObjectMethodRetBool(void *obj, int func) const; + int CallObjectMethodRetInt(void *obj, int func) const; + void *CallObjectMethodRetPtr(void *obj, int func) const; + void *CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const; + void CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; + bool CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; + int CallScriptObjectMethod(void *obj, int func); + + void ConstructScriptObjectCopy(void *mem, void *obj, asCObjectType *type); + + void DeleteDiscardedModules(); + + void RemoveTemplateInstanceType(asCObjectType *t); + + asCConfigGroup *FindConfigGroupForFunction(int funcId) const; + asCConfigGroup *FindConfigGroupForGlobalVar(int gvarId) const; + asCConfigGroup *FindConfigGroupForTypeInfo(const asCTypeInfo *type) const; + asCConfigGroup *FindConfigGroupForFuncDef(const asCFuncdefType *funcDef) const; + + int RequestBuild(); + void BuildCompleted(); + + void PrepareEngine(); + bool isPrepared; + + int CreateContext(asIScriptContext **context, bool isInternal); + + asCTypeInfo *GetRegisteredType(const asCString &name, asSNameSpace *ns) const; + + asCObjectType *GetListPatternType(int listPatternFuncId); + void DestroyList(asBYTE *buffer, const asCObjectType *listPatternType); + void DestroySubList(asBYTE *&buffer, asSListPatternNode *&patternNode); + + int AddBehaviourFunction(asCScriptFunction &func, asSSystemFunctionInterface &internal); + + asCString GetFunctionDeclaration(int funcId); + + asCScriptFunction *GetScriptFunction(int funcId) const; + + asCModule *GetModule(const char *name, bool create); + asCModule *GetModuleFromFuncId(int funcId); + + int GetMethodIdByDecl(const asCObjectType *ot, const char *decl, asCModule *mod); + int GetFactoryIdByDecl(const asCObjectType *ot, const char *decl); + + int GetNextScriptFunctionId(); + void AddScriptFunction(asCScriptFunction *func); + void RemoveScriptFunction(asCScriptFunction *func); + void RemoveFuncdef(asCFuncdefType *func); + + int ConfigError(int err, const char *funcName, const char *arg1, const char *arg2); + + int GetTypeIdFromDataType(const asCDataType &dt) const; + asCDataType GetDataTypeFromTypeId(int typeId) const; + asCObjectType *GetObjectTypeFromTypeId(int typeId) const; + void RemoveFromTypeIdMap(asCTypeInfo *type); + + bool IsTemplateType(const char *name) const; + int SetTemplateRestrictions(asCObjectType *templateType, asCScriptFunction *func, const char *caller, const char *decl); + asCObjectType *GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes, asCModule *requestingModule); + asCScriptFunction *GenerateTemplateFactoryStub(asCObjectType *templateType, asCObjectType *templateInstanceType, int origFactoryId); + bool GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *templateInstanceType, asCScriptFunction *templateFunc, asCScriptFunction **newFunc); + asCFuncdefType *GenerateNewTemplateFuncdef(asCObjectType *templateType, asCObjectType *templateInstanceType, asCFuncdefType *templateFuncdef); + asCDataType DetermineTypeForTemplate(const asCDataType &orig, asCObjectType *tmpl, asCObjectType *ot); + bool RequireTypeReplacement(asCDataType &type, asCObjectType *templateType); + + asCModule *FindNewOwnerForSharedType(asCTypeInfo *type, asCModule *mod); + asCModule *FindNewOwnerForSharedFunc(asCScriptFunction *func, asCModule *mod); + + asCFuncdefType *FindMatchingFuncdef(asCScriptFunction *func, asCModule *mod); + + // String constants + // TODO: Must free unused string constants, thus the ref count for each must be tracked + int AddConstantString(const char *str, size_t length); + const asCString &GetConstantString(int id); + + // Global property management + asCGlobalProperty *AllocateGlobalProperty(); + void RemoveGlobalProperty(asCGlobalProperty *prop); + + int GetScriptSectionNameIndex(const char *name); + + // Namespace management + asSNameSpace *AddNameSpace(const char *name); + asSNameSpace *FindNameSpace(const char *name) const; + asSNameSpace *GetParentNameSpace(asSNameSpace *ns) const; + +//=========================================================== +// internal properties +//=========================================================== + asCMemoryMgr memoryMgr; + + asUINT initialContextStackSize; + + asCObjectType *defaultArrayObjectType; + asCObjectType scriptTypeBehaviours; + asCObjectType functionBehaviours; + + // Registered interface + asCArray registeredObjTypes; // doesn't increase ref count + asCArray registeredTypeDefs; // doesn't increase ref count + asCArray registeredEnums; // doesn't increase ref count + // TODO: memory savings: Since there can be only one property with the same name a simpler symbol table should be used for global props + asCSymbolTable registeredGlobalProps; // increases ref count + asCSymbolTable registeredGlobalFuncs; + asCArray registeredFuncDefs; // doesn't increase ref count + asCArray registeredTemplateTypes; // doesn't increase ref count + asCScriptFunction *stringFactory; + bool configFailed; + + // Stores all registered types + asCMap allRegisteredTypes; // increases ref count + + // Dummy types used to name the subtypes in the template objects + asCArray templateSubTypes; + + // Store information about template types + // This list will contain all instances of templates, both registered specialized + // types and those automacially instantiated from scripts + asCArray templateInstanceTypes; // increases ref count + + // Store information about list patterns + asCArray listPatternTypes; // increases ref count + + // Stores all global properties, both those registered by application, and those declared by scripts. + // The id of a global property is the index in this array. + asCArray globalProperties; // increases ref count + asCArray freeGlobalPropertyIds; + + // This map is used to quickly find a property by its memory address + // It is used principally during building, cleanup, and garbage detection for script functions + asCMap varAddressMap; // doesn't increase ref count + + // Stores all functions, i.e. registered functions, script functions, class methods, behaviours, etc. + asCArray scriptFunctions; // doesn't increase ref count + asCArray freeScriptFunctionIds; + asCArray signatureIds; + + // An array with all module imported functions + asCArray importedFunctions; // doesn't increase ref count + asCArray freeImportedFunctionIdxs; + + // Synchronized + mutable asCAtomic refCount; + // Synchronized with engineRWLock + // This array holds all live script modules + asCArray scriptModules; + // Synchronized with engineRWLock + // This is a pointer to the last module that was requested. It is used for performance + // improvement, since it is common that the same module is accessed many times in a row + asCModule *lastModule; + // Synchronized with engineRWLock + // This flag is true if a script is currently being compiled. It is used to prevent multiple + // threads from requesting builds at the same time (without blocking) + bool isBuilding; + // Synchronized with engineRWLock + // This array holds modules that have been discard (thus are no longer visible to the application) + // but cannot yet be deleted due to having external references to some of the entities in them + asCArray discardedModules; + // This flag is set to true during compilations of scripts (or loading pre-compiled scripts) + // to delay the validation of template types until the subtypes have been fully declared + bool deferValidationOfTemplateTypes; + + // Tokenizer is instantiated once to share resources + asCTokenizer tok; + + // Stores shared script declared types (classes, interfaces, enums) + asCArray sharedScriptTypes; // increases ref count + // This array stores the template instances types that have been automatically generated from template types + asCArray generatedTemplateTypes; + // Stores the funcdefs + // TODO: redesign: Only shared funcdefs should be stored here + // a funcdef becomes shared if all arguments and the return type are shared (or application registered) + asCArray funcDefs; // doesn't increases ref count + + // Stores the names of the script sections for debugging purposes + asCArray scriptSectionNames; + + // Type identifiers + mutable int typeIdSeqNbr; + mutable asCMap mapTypeIdToTypeInfo; + + // Garbage collector + asCGarbageCollector gc; + + // Dynamic groups + asCConfigGroup defaultGroup; + asCArray configGroups; + asCConfigGroup *currentGroup; + asDWORD defaultAccessMask; + asSNameSpace *defaultNamespace; + + // Message callback + bool msgCallback; + asSSystemFunctionInterface msgCallbackFunc; + void *msgCallbackObj; + struct preMessage_t + { + preMessage_t() { isSet = false; } + bool isSet; + asCString message; + asCString scriptname; + int r; + int c; + } preMessage; + + // JIt compilation + asIJITCompiler *jitCompiler; + + // Namespaces + // These are shared between all entities and are + // only deleted once the engine is destroyed + asCArray nameSpaces; + + // String constants + // These are shared between all scripts and are + // only deleted once the engine is destroyed + asCArray stringConstants; + asCMap stringToIdMap; + + // Callbacks for context pooling + asREQUESTCONTEXTFUNC_t requestCtxFunc; + asRETURNCONTEXTFUNC_t returnCtxFunc; + void *ctxCallbackParam; + + // User data + asCArray userData; + + struct SEngineClean { asPWORD type; asCLEANENGINEFUNC_t cleanFunc; }; + asCArray cleanEngineFuncs; + struct SModuleClean { asPWORD type; asCLEANMODULEFUNC_t cleanFunc; }; + asCArray cleanModuleFuncs; + struct SContextClean { asPWORD type; asCLEANCONTEXTFUNC_t cleanFunc; }; + asCArray cleanContextFuncs; + struct SFunctionClean { asPWORD type; asCLEANFUNCTIONFUNC_t cleanFunc; }; + asCArray cleanFunctionFuncs; + struct STypeInfoClean { asPWORD type; asCLEANTYPEINFOFUNC_t cleanFunc; }; + asCArray cleanTypeInfoFuncs; + struct SScriptObjClean { asPWORD type; asCLEANSCRIPTOBJECTFUNC_t cleanFunc; }; + asCArray cleanScriptObjectFuncs; + + // Synchronization for threads + DECLAREREADWRITELOCK(mutable engineRWLock) + + // Engine properties + struct + { + bool allowUnsafeReferences; + bool optimizeByteCode; + bool copyScriptSections; + asUINT maximumContextStackSize; + bool useCharacterLiterals; + bool allowMultilineStrings; + bool allowImplicitHandleTypes; + bool buildWithoutLineCues; + bool initGlobalVarsAfterBuild; + bool requireEnumScope; + int scanner; + bool includeJitInstructions; + int stringEncoding; + int propertyAccessorMode; + bool expandDefaultArrayToTemplate; + bool autoGarbageCollect; + bool disallowGlobalVars; + bool alwaysImplDefaultConstruct; + int compilerWarnings; + bool disallowValueAssignForRefType; + // TODO: 3.0.0: Remove the alterSyntaxNamedArgs + int alterSyntaxNamedArgs; + bool disableIntegerDivision; + bool disallowEmptyListElements; + // TODO: 3.0.0: Remove the privatePropAsProtected + bool privatePropAsProtected; + bool allowUnicodeIdentifiers; + int heredocTrimMode; + } ep; + + // This flag is to allow a quicker shutdown when releasing the engine + bool shuttingDown; + + // This flag is set when the engine's destructor is called, this is to + // avoid recursive calls if an object happens to increment/decrement + // the ref counter during shutdown + bool inDestructor; +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_scriptfunction.h b/3rdparty/angelscript/include/as_scriptfunction.h new file mode 100644 index 0000000..b2e5654 --- /dev/null +++ b/3rdparty/angelscript/include/as_scriptfunction.h @@ -0,0 +1,343 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptfunction.h +// +// A container for a compiled script function +// + + + +#ifndef AS_SCRIPTFUNCTION_H +#define AS_SCRIPTFUNCTION_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCModule; +class asCConfigGroup; +class asCGlobalProperty; +class asCScriptNode; +class asCFuncdefType; +struct asSNameSpace; + +struct asSScriptVariable +{ + asCString name; + asCDataType type; + int stackOffset; + asUINT declaredAtProgramPos; +}; + +enum asEListPatternNodeType +{ + asLPT_REPEAT, + asLPT_REPEAT_SAME, + asLPT_START, + asLPT_END, + asLPT_TYPE +}; + +struct asSListPatternNode +{ + asSListPatternNode(asEListPatternNodeType t) : type(t), next(0) {} + virtual ~asSListPatternNode() {}; + virtual asSListPatternNode *Duplicate() { return asNEW(asSListPatternNode)(type); } + asEListPatternNodeType type; + asSListPatternNode *next; +}; + +struct asSListPatternDataTypeNode : public asSListPatternNode +{ + asSListPatternDataTypeNode(const asCDataType &dt) : asSListPatternNode(asLPT_TYPE), dataType(dt) {} + asSListPatternNode *Duplicate() { return asNEW(asSListPatternDataTypeNode)(dataType); } + asCDataType dataType; +}; + +enum asEObjVarInfoOption +{ + asOBJ_UNINIT, + asOBJ_INIT, + asBLOCK_BEGIN, + asBLOCK_END +}; + +struct asSObjectVariableInfo +{ + asUINT programPos; + int variableOffset; + asEObjVarInfoOption option; +}; + +struct asSSystemFunctionInterface; + +// TODO: Might be interesting to allow enumeration of accessed global variables, and +// also functions/methods that are being called. This could be used to build a +// code database with call graphs, etc. + +void RegisterScriptFunction(asCScriptEngine *engine); + +class asCScriptFunction : public asIScriptFunction +{ +public: + // From asIScriptFunction + asIScriptEngine *GetEngine() const; + + // Memory management + int AddRef() const; + int Release() const; + + // Miscellaneous + int GetId() const; + asEFuncType GetFuncType() const; + const char *GetModuleName() const; + asIScriptModule *GetModule() const; + const char *GetScriptSectionName() const; + const char *GetConfigGroup() const; + asDWORD GetAccessMask() const; + void *GetAuxiliary() const; + + // Function signature + asITypeInfo *GetObjectType() const; + const char *GetObjectName() const; + const char *GetName() const; + const char *GetNamespace() const; + const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const; + bool IsReadOnly() const; + bool IsPrivate() const; + bool IsProtected() const; + bool IsFinal() const; + bool IsOverride() const; + bool IsShared() const; + asUINT GetParamCount() const; + int GetParam(asUINT index, int *typeId, asDWORD *flags = 0, const char **name = 0, const char **defaultArg = 0) const; +#ifdef AS_DEPRECATED + // Deprecated, since 2.29.0, 2014-04-06 + int GetParamTypeId(asUINT index, asDWORD *flags = 0) const; +#endif + int GetReturnTypeId(asDWORD *flags = 0) const; + + // Type id for function pointers + int GetTypeId() const; + bool IsCompatibleWithTypeId(int typeId) const; + + // Delegates + void *GetDelegateObject() const; + asITypeInfo *GetDelegateObjectType() const; + asIScriptFunction *GetDelegateFunction() const; + + // Debug information + asUINT GetVarCount() const; + int GetVar(asUINT index, const char **name, int *typeId = 0) const; + const char * GetVarDecl(asUINT index, bool includeNamespace = false) const; + int FindNextLineWithCode(int line) const; + + // For JIT compilation + asDWORD *GetByteCode(asUINT *length = 0); + + // User data + void *SetUserData(void *userData, asPWORD type); + void *GetUserData(asPWORD type) const; + +public: + //----------------------------------- + // Internal methods + + asCScriptFunction(asCScriptEngine *engine, asCModule *mod, asEFuncType funcType); + ~asCScriptFunction(); + + // Keep an internal reference counter to separate references coming from + // application or script objects and references coming from the script code + int AddRefInternal(); + int ReleaseInternal(); + + void DestroyHalfCreated(); + + // TODO: operator== + // TODO: The asIScriptFunction should provide operator== and operator!= that should do a + // a value comparison. Two delegate objects that point to the same object and class method should compare as equal + // TODO: The operator== should also be provided in script as opEquals to allow the same comparison in script + // To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods + // Perhaps reusing 'auto' to mean the same type as the object + //bool operator==(const asCScriptFunction &other) const; + + void DestroyInternal(); + + void AddVariable(asCString &name, asCDataType &type, int stackOffset); + + int GetSpaceNeededForArguments(); + int GetSpaceNeededForReturnValue(); + asCString GetDeclarationStr(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const; + int GetLineNumber(int programPosition, int *sectionIdx); + void ComputeSignatureId(); + bool IsSignatureEqual(const asCScriptFunction *func) const; + bool IsSignatureExceptNameEqual(const asCScriptFunction *func) const; + bool IsSignatureExceptNameEqual(const asCDataType &retType, const asCArray ¶mTypes, const asCArray &inOutFlags, const asCObjectType *type, bool isReadOnly) const; + bool IsSignatureExceptNameAndReturnTypeEqual(const asCScriptFunction *fun) const; + bool IsSignatureExceptNameAndReturnTypeEqual(const asCArray ¶mTypes, const asCArray &inOutFlags, const asCObjectType *type, bool isReadOnly) const; + bool IsSignatureExceptNameAndObjectTypeEqual(const asCScriptFunction *func) const; + + asCTypeInfo *GetTypeInfoOfLocalVar(short varOffset); + + void MakeDelegate(asCScriptFunction *func, void *obj); + + int RegisterListPattern(const char *decl, asCScriptNode *listPattern); + int ParseListPattern(asSListPatternNode *&target, const char *decl, asCScriptNode *listPattern); + + bool DoesReturnOnStack() const; + + void JITCompile(); + + void AddReferences(); + void ReleaseReferences(); + + void AllocateScriptFunctionData(); + void DeallocateScriptFunctionData(); + + asCGlobalProperty *GetPropertyByGlobalVarPtr(void *gvarPtr); + + // GC methods (for delegates) + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +public: + //----------------------------------- + // Properties + + mutable asCAtomic externalRefCount; // Used for external referneces + asCAtomic internalRefCount; // Used for internal references + mutable bool gcFlag; + asCScriptEngine *engine; + asCModule *module; + + asCArray userData; + + // Function signature + asCString name; + asCDataType returnType; + asCArray parameterTypes; + asCArray parameterNames; + asCArray inOutFlags; + asCArray defaultArgs; + bool isReadOnly; + bool isPrivate; + bool isProtected; + bool isFinal; + bool isOverride; + asCObjectType *objectType; + int signatureId; + + int id; + + asEFuncType funcType; + asDWORD accessMask; + bool isShared; + + // Namespace will be null for funcdefs that are declared as child funcdefs + // of a class. In this case the namespace shall be taken from the parentClass + // in the funcdefType + asSNameSpace *nameSpace; + + asCFuncdefType *funcdefType; // Doesn't increase refCount + + // Used by asFUNC_DELEGATE + void *objForDelegate; + asCScriptFunction *funcForDelegate; + + // Used by list factory behaviour + asSListPatternNode *listPattern; + + // Used by asFUNC_SCRIPT + struct ScriptFunctionData + { + // Bytecode for the script function + asCArray byteCode; + + // The stack space needed for the local variables + asDWORD variableSpace; + + // These hold information on objects and function pointers, including temporary + // variables used by exception handler and when saving bytecode + asCArray objVariableTypes; + asCArray objVariablePos; + + // The first variables in above array are allocated on the heap, the rest on the stack. + // This variable shows how many are on the heap. + asUINT objVariablesOnHeap; + + // Holds information on scope for object variables on the stack + asCArray objVariableInfo; + + // The stack needed to execute the function + int stackNeeded; + + // JIT compiled code of this function + asJITFunction jitFunction; + + // Holds debug information on explicitly declared variables + asCArray variables; + // Store position, line number pairs for debug information + asCArray lineNumbers; + // Store the script section where the code was declared + int scriptSectionIdx; + // Store the location where the function was declared (row in the lower 20 bits, and column in the upper 12) + int declaredAt; + // Store position/index pairs if the bytecode is compiled from multiple script sections + asCArray sectionIdxs; + }; + ScriptFunctionData *scriptData; + + // Stub functions and delegates don't own the object and parameters + bool dontCleanUpOnException; + + // Used by asFUNC_VIRTUAL + int vfTableIdx; + + // Used by asFUNC_SYSTEM + asSSystemFunctionInterface *sysFuncIntf; +}; + +const char * const DELEGATE_FACTORY = "$dlgte"; +asCScriptFunction *CreateDelegate(asCScriptFunction *func, void *obj); + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_scriptnode.h b/3rdparty/angelscript/include/as_scriptnode.h new file mode 100644 index 0000000..4f32c92 --- /dev/null +++ b/3rdparty/angelscript/include/as_scriptnode.h @@ -0,0 +1,137 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptnode.h +// +// A node in the script tree built by the parser for compilation +// + + +#ifndef AS_SCRIPTNODE_H +#define AS_SCRIPTNODE_H + +#include "as_config.h" +#include "as_tokendef.h" + +BEGIN_AS_NAMESPACE + +enum eScriptNode +{ + snUndefined, + snScript, + snFunction, + snConstant, + snDataType, + snIdentifier, + snParameterList, + snStatementBlock, + snDeclaration, + snExpressionStatement, + snIf, + snFor, + snWhile, + snReturn, + snExpression, + snExprTerm, + snFunctionCall, + snConstructCall, + snArgList, + snExprPreOp, + snExprPostOp, + snExprOperator, + snExprValue, + snBreak, + snContinue, + snDoWhile, + snAssignment, + snCondition, + snSwitch, + snCase, + snImport, + snClass, + snInitList, + snInterface, + snEnum, + snTypedef, + snCast, + snVariableAccess, + snFuncDef, + snVirtualProperty, + snNamespace, + snMixin, + snListPattern, + snNamedArgument, + snScope +}; + +struct sToken +{ + eTokenType type; + size_t pos; + size_t length; +}; + +class asCScriptEngine; + +class asCScriptNode +{ +public: + asCScriptNode(eScriptNode nodeType); + + void Destroy(asCScriptEngine *engine); + asCScriptNode *CreateCopy(asCScriptEngine *engine); + + void SetToken(sToken *token); + void AddChildLast(asCScriptNode *node); + void DisconnectParent(); + + void UpdateSourcePos(size_t pos, size_t length); + + eScriptNode nodeType; + eTokenType tokenType; + size_t tokenPos; + size_t tokenLength; + + asCScriptNode *parent; + asCScriptNode *next; + asCScriptNode *prev; + asCScriptNode *firstChild; + asCScriptNode *lastChild; + +protected: + // Must call Destroy instead + ~asCScriptNode() {} +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_scriptobject.h b/3rdparty/angelscript/include/as_scriptobject.h new file mode 100644 index 0000000..6d6b3a2 --- /dev/null +++ b/3rdparty/angelscript/include/as_scriptobject.h @@ -0,0 +1,163 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_scriptobject.h +// +// A generic class for handling script declared structures +// + + + +#ifndef AS_SCRIPTOBJECT_H +#define AS_SCRIPTOBJECT_H + +#include "as_config.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +class asCObjectType; + +// TODO: Add const overload for GetAddressOfProperty + +// TODO: weak: Should move to its own file +class asCLockableSharedBool : public asILockableSharedBool +{ +public: + asCLockableSharedBool(); + int AddRef() const; + int Release() const; + + bool Get() const; + void Set(bool); + + void Lock() const; + void Unlock() const; + +protected: + mutable asCAtomic refCount; + bool value; + DECLARECRITICALSECTION(mutable lock); +}; + +class asCScriptObject : public asIScriptObject +{ +public: +//=================================== +// From asIScriptObject +//=================================== + + // Memory management + int AddRef() const; + int Release() const; + asILockableSharedBool *GetWeakRefFlag() const; + + // Type info + int GetTypeId() const; + asITypeInfo *GetObjectType() const; + + // Class properties + asUINT GetPropertyCount() const; + int GetPropertyTypeId(asUINT prop) const; + const char *GetPropertyName(asUINT prop) const; + void *GetAddressOfProperty(asUINT prop); + + // Miscellaneous + asIScriptEngine *GetEngine() const; + int CopyFrom(asIScriptObject *other); + + // User data + void *SetUserData(void *data, asPWORD type = 0); + void *GetUserData(asPWORD type = 0) const; + +//==================================== +// Internal +//==================================== + asCScriptObject(asCObjectType *objType, bool doInitialize = true); + virtual ~asCScriptObject(); + + asCScriptObject &operator=(const asCScriptObject &other); + + // GC methods + void Destruct(); + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + + // Used for properties + void *AllocateUninitializedObject(asCObjectType *objType, asCScriptEngine *engine); + void FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine); + void CopyObject(void *src, void *dst, asCObjectType *objType, asCScriptEngine *engine); + void CopyHandle(asPWORD *src, asPWORD *dst, asCObjectType *objType, asCScriptEngine *engine); + + void CallDestructor(); + +//============================================= +// Properties +//============================================= +protected: + friend class asCContext; + asCObjectType *objType; + + mutable asCAtomic refCount; + mutable asBYTE gcFlag:1; + mutable asBYTE hasRefCountReachedZero:1; + bool isDestructCalled; + + // Most script classes instances won't have neither the weakRefFlags nor + // userData so we only allocate this if requested. Even when used it is + // not something that will be accessed all the time so having the extra + // indirection will not affect the performance significantly. + struct SExtra + { + SExtra() : weakRefFlag(0) {}; + asCLockableSharedBool *weakRefFlag; + asCArray userData; + }; + mutable SExtra *extra; +}; + +void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self); +asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self); + +void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self); + +void RegisterScriptObject(asCScriptEngine *engine); + +asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_string.h b/3rdparty/angelscript/include/as_string.h new file mode 100644 index 0000000..0ee7469 --- /dev/null +++ b/3rdparty/angelscript/include/as_string.h @@ -0,0 +1,134 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// This class has been designed to be easy to use, but not necessarily efficiency. +// It doesn't use shared string memory, or reference counting. It keeps track of +// string length, memory size. It also makes sure that the string is null-terminated. + +#ifndef AS_STRING_H +#define AS_STRING_H + +#include +#include + +class asCString +{ +public: + asCString(); + ~asCString(); + +#ifdef AS_CAN_USE_CPP11 + asCString(asCString &&); + asCString &operator =(asCString &&); +#endif // c++11 + + asCString(const asCString &); + asCString(const char *); + asCString(const char *, size_t length); + explicit asCString(char); + + void Allocate(size_t len, bool keepData); + void SetLength(size_t len); + size_t GetLength() const; + + void Concatenate(const char *str, size_t length); + asCString &operator +=(const asCString &); + asCString &operator +=(const char *); + asCString &operator +=(char); + + void Assign(const char *str, size_t length); + asCString &operator =(const asCString &); + asCString &operator =(const char *); + asCString &operator =(char); + + asCString SubString(size_t start, size_t length = (size_t)(-1)) const; + + int FindLast(const char *str, int *count = 0) const; + + size_t Format(const char *fmt, ...); + + int Compare(const char *str) const; + int Compare(const asCString &str) const; + int Compare(const char *str, size_t length) const; + + char *AddressOf(); + const char *AddressOf() const; + char &operator [](size_t index); + const char &operator[](size_t index) const; + size_t RecalculateLength(); + +protected: + unsigned int length; + union + { + char *dynamic; + char local[12]; + }; +}; + +// Helper functions + +bool operator ==(const asCString &, const asCString &); +bool operator !=(const asCString &, const asCString &); + +bool operator ==(const asCString &, const char *); +bool operator !=(const asCString &, const char *); + +bool operator ==(const char *, const asCString &); +bool operator !=(const char *, const asCString &); + +bool operator <(const asCString &, const asCString &); + +asCString operator +(const asCString &, const char *); +asCString operator +(const char *, const asCString &); +asCString operator +(const asCString &, const asCString &); + +// a wrapper for using the pointer of asCString in asCMap +class asCStringPointer +{ +public: + asCStringPointer(); + asCStringPointer(const char *str, size_t len); + asCStringPointer(asCString *cstr); + + const char *AddressOf() const; + size_t GetLength() const; + + bool operator==(const asCStringPointer& other) const; + bool operator<(const asCStringPointer& other) const; + +private: + // Either string/length or cstring is stored + const char *string; + size_t length; + asCString *cstring; +}; + +#endif diff --git a/3rdparty/angelscript/include/as_string_util.h b/3rdparty/angelscript/include/as_string_util.h new file mode 100644 index 0000000..29174df --- /dev/null +++ b/3rdparty/angelscript/include/as_string_util.h @@ -0,0 +1,51 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#ifndef AS_STRING_UTIL_H +#define AS_STRING_UTIL_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +int asCompareStrings(const char *str1, size_t len1, const char *str2, size_t len2); + +double asStringScanDouble(const char *string, size_t *numScanned); +asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned, bool *overflow); + +int asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer); +int asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength); + +int asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer); + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_symboltable.h b/3rdparty/angelscript/include/as_symboltable.h new file mode 100644 index 0000000..11a7115 --- /dev/null +++ b/3rdparty/angelscript/include/as_symboltable.h @@ -0,0 +1,567 @@ +/* + AngelCode Scripting Library + Copyright (c) 2012-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_symboltable.h +// +// Created on: Jun 19, 2012 +// Author: Markus Lenger, a.k.a. mlengerx +// +// This class is used for fast symbol lookups while parsing or loading bytecode +// + +#ifndef AS_SYMBOLTABLE_H +#define AS_SYMBOLTABLE_H + +#include "as_config.h" +#include "as_memory.h" +#include "as_string.h" +#include "as_map.h" +#include "as_datatype.h" +#include "as_namespace.h" + + +BEGIN_AS_NAMESPACE + + + + + +// Interface to avoid nested templates which is not well supported by older compilers, e.g. MSVC6 +struct asIFilter +{ + virtual bool operator()(const void*) const = 0; + virtual ~asIFilter() {}; +}; + + + + +// forward declaration +template +class asCSymbolTable; + + + + +// Iterator that allows iterating in index order +template +class asCSymbolTableIterator +{ +public: + T2* operator*() const; + T2* operator->() const; + asCSymbolTableIterator& operator++(int); + asCSymbolTableIterator& operator--(int); + operator bool() const; + int GetIndex() const { return m_idx; } + +private: + friend class asCSymbolTable; + asCSymbolTableIterator(asCSymbolTable *table); + + void Next(); + void Previous(); + + asCSymbolTable* m_table; + unsigned int m_idx; +}; + + + + +// Symbol table mapping namespace + name to symbols +// The structure keeps the entries indexed in an array so the indices will not change +// There is also a map for a quick lookup. The map supports multiple entries with the same name +template +class asCSymbolTable +{ +public: + typedef asCSymbolTableIterator iterator; + typedef asCSymbolTableIterator const_iterator; + + asCSymbolTable(asUINT initialCapacity = 0); + + int GetFirstIndex(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const; + int GetFirstIndex(const asSNameSpace *ns, const asCString &name) const; + int GetLastIndex() const; + + int GetIndex(const T*) const; + + T* GetFirst(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const; + T* GetFirst(const asSNameSpace *ns, const asCString &name); + const T* GetFirst(const asSNameSpace *ns, const asCString &name) const; + T* Get(asUINT index); + const T* Get(asUINT index) const; + T* GetLast(); + const T* GetLast() const; + + const asCArray &GetIndexes(const asSNameSpace *ns, const asCString &name) const; + + asUINT Put(T* entry); + + asUINT GetSize() const; + + void SwapWith(asCSymbolTable &other); + + void Clear(); + bool Erase(asUINT idx); + void Allocate(asUINT elem_cnt, bool keep_data); + + iterator List(); + const_iterator List() const; + +private: + // Don't allow assignment + asCSymbolTable& operator=(const asCSymbolTable &other) { return *this; } + + friend class asCSymbolTableIterator; + friend class asCSymbolTableIterator; + + void GetKey(const T *entry, asSNameSpaceNamePair &key) const; + bool CheckIdx(asUINT idx) const; + + asCMap > m_map; + asCArray m_entries; + unsigned int m_size; +}; + + + + +template +void asCSymbolTable::SwapWith(asCSymbolTable &other) +{ + m_map.SwapWith(other.m_map); + m_entries.SwapWith(other.m_entries); + + asUINT tmp = m_size; + m_size = other.m_size; + other.m_size = tmp; +} + + + + +// Constructor +// initialCapacity gives the number of entries to allocate in advance +template +asCSymbolTable::asCSymbolTable(asUINT initialCapacity) : m_entries(initialCapacity) +{ + m_size = 0; +} + + + +template +int asCSymbolTable::GetFirstIndex( + const asSNameSpace *ns, + const asCString &name, + const asIFilter &filter) const +{ + asSNameSpaceNamePair key(ns, name); + + asSMapNode > *cursor; + if( m_map.MoveTo(&cursor, key) ) + { + const asCArray &arr = m_map.GetValue(cursor); + for( asUINT n = 0; n < arr.GetLength(); n++ ) + { + T *entry = m_entries[arr[n]]; + if( entry && filter(entry) ) + return arr[n]; + } + } + + return -1; +} + + + +template +const asCArray &asCSymbolTable::GetIndexes(const asSNameSpace *ns, const asCString &name) const +{ + asSNameSpaceNamePair key(ns, name); + + asSMapNode > *cursor; + if( m_map.MoveTo(&cursor, key) ) + return m_map.GetValue(cursor); + + static asCArray dummy; + return dummy; +} + + + + +template +T* asCSymbolTable::GetFirst(const asSNameSpace *ns, const asCString &name, const asIFilter &comp) const +{ + int idx = GetFirstIndex(ns, name, comp); + if (idx != -1) return m_entries[idx]; + return 0; +} + + + + +template +int asCSymbolTable::GetFirstIndex(const asSNameSpace *ns, const asCString &name) const +{ + asSNameSpaceNamePair key(ns, name); + + asSMapNode > *cursor; + if( m_map.MoveTo(&cursor, key) ) + return m_map.GetValue(cursor)[0]; + + return -1; +} + + + + +// Find the index of a certain symbol +// ATTENTION: this function has linear runtime complexity O(n)!! +template +int asCSymbolTable::GetIndex(const T* entry) const +{ + for( asUINT n = 0; n < m_entries.GetLength(); n++ ) + if( m_entries[n] == entry ) + return n; + + return -1; +} + + + + + + +template +T* asCSymbolTable::Get(asUINT idx) +{ + if( !CheckIdx(idx) ) + return 0; + + return m_entries[idx]; +} + +template +const T* asCSymbolTable::Get(asUINT idx) const +{ + return const_cast< asCSymbolTable* >(this)->Get(idx); +} + + + + + +template +T* asCSymbolTable::GetFirst(const asSNameSpace *ns, const asCString &name) +{ + int idx = GetFirstIndex(ns, name); + return Get(idx); +} + +template +const T* asCSymbolTable::GetFirst(const asSNameSpace *ns, const asCString &name) const +{ + return const_cast< asCSymbolTable* >(this)->GetFirst(ns, name); +} + + + + + +template +T* asCSymbolTable::GetLast() +{ + return Get(GetLastIndex()); +} + +template +const T* asCSymbolTable::GetLast() const +{ + return const_cast< asCSymbolTable* >(this)->GetLast(); +} + + + + + +// Clear the symbol table +// ATTENTION: The contained symbols are not rleased. This is up to the client +template +void asCSymbolTable::Clear() +{ + m_entries.SetLength(0); + m_map.EraseAll(); + m_size = 0; +} + + + + +// Pre-allocate slots for elemCnt entries +template +void asCSymbolTable::Allocate(asUINT elemCnt, bool keepData) +{ + asASSERT( elemCnt >= m_entries.GetLength() ); + m_entries.Allocate(elemCnt, keepData); + if( !keepData ) + m_map.EraseAll(); +} + + + +template +bool asCSymbolTable::Erase(asUINT idx) +{ + if( !CheckIdx(idx) ) + { + asASSERT(false); + return false; + } + + T *entry = m_entries[idx]; + asASSERT(entry); + if( !entry ) + return false; + + // Remove the symbol from the lookup map + asSNameSpaceNamePair key; + GetKey(entry, key); + + asSMapNode > *cursor; + if( m_map.MoveTo(&cursor, key) ) + { + asCArray &arr = m_map.GetValue(cursor); + arr.RemoveValue(idx); + if( arr.GetLength() == 0 ) + m_map.Erase(cursor); + } + else + asASSERT(false); + + // Remove the symbol from the indexed array + if( idx == m_entries.GetLength() - 1 ) + m_entries.PopLast(); + else + { + // Must keep the array packed + int prevIdx = int(m_entries.GetLength()-1); + m_entries[idx] = m_entries.PopLast(); + + // Update the index in the lookup map + entry = m_entries[idx]; + GetKey(entry, key); + if( m_map.MoveTo(&cursor, key) ) + { + asCArray &arr = m_map.GetValue(cursor); + arr[arr.IndexOf(prevIdx)] = idx; + } + else + asASSERT(false); + } + m_size--; + + return true; +} + + + + +template +asUINT asCSymbolTable::Put(T *entry) +{ + asUINT idx = m_entries.GetLength(); + asSNameSpaceNamePair key; + GetKey(entry, key); + + asSMapNode > *cursor; + if( m_map.MoveTo(&cursor, key) ) + m_map.GetValue(cursor).PushLast(idx); + else + { + asCArray arr(1); + arr.PushLast(idx); + m_map.Insert(key, arr); + } + + m_entries.PushLast(entry); + m_size++; + return idx; +} + + + + +// Return key for specified symbol (namespace and name are used to generate the key) +template +void asCSymbolTable::GetKey(const T *entry, asSNameSpaceNamePair &key) const +{ + key = asSNameSpaceNamePair(entry->nameSpace, entry->name); +} + + + + +template +asUINT asCSymbolTable::GetSize() const +{ + return m_size; +} + + + + +template +bool asCSymbolTable::CheckIdx(asUINT idx) const +{ + return idx < m_entries.GetLength(); +} + + + + +template +int asCSymbolTable::GetLastIndex() const +{ + int idx = int(m_entries.GetLength()) - 1; + asASSERT( idx == -1 || m_entries[idx] ); + return idx; +} + + + + +template +asCSymbolTableIterator asCSymbolTable::List() +{ + return asCSymbolTableIterator(this); +} + + + + +template +typename asCSymbolTable::const_iterator asCSymbolTable::List() const +{ + return asCSymbolTableIterator(const_cast< asCSymbolTable *>(this)); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Iterator + + +template +asCSymbolTableIterator::asCSymbolTableIterator(asCSymbolTable *table) : m_table(table), m_idx(0) +{ + asUINT sz = m_table->m_entries.GetLength(); + while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) + m_idx++; +} + + + +template +T2* asCSymbolTableIterator::operator*() const +{ + asASSERT(m_table->CheckIdx(m_idx)); + return m_table->m_entries[m_idx]; +} + + + +template +T2* asCSymbolTableIterator::operator->() const +{ + asASSERT(m_table->CheckIdx(m_idx)); + return m_table->m_entries[m_idx]; +} + + + +template +asCSymbolTableIterator& asCSymbolTableIterator::operator++(int) +{ + Next(); + return *this; +} + + + +// Return true if more elements are following +// ATTENTION: When deleting the object currently pointed to by this iterator this +// method returns false even though there might be more elements in the list +template +asCSymbolTableIterator::operator bool() const +{ + return m_idx < m_table->m_entries.GetLength() && m_table->m_entries[m_idx] != 0; +} + + + +template +void asCSymbolTableIterator::Next() +{ + asUINT sz = m_table->m_entries.GetLength(); + m_idx++; + while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) + m_idx++; +} + + + +template +void asCSymbolTableIterator::Previous() +{ + // overflow on stepping over first element + asUINT sz = m_table->m_entries.GetLength(); + m_idx--; + while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) + m_idx--; +} + + + +template +asCSymbolTableIterator& asCSymbolTableIterator::operator--(int) +{ + Previous(); + return *this; +} + + +END_AS_NAMESPACE + +#endif // AS_SYMBOLTABLE_H diff --git a/3rdparty/angelscript/include/as_texts.h b/3rdparty/angelscript/include/as_texts.h new file mode 100644 index 0000000..d5f7d82 --- /dev/null +++ b/3rdparty/angelscript/include/as_texts.h @@ -0,0 +1,349 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_texts.h +// +// These are text strings used through out the library +// + + +#ifndef AS_TEXTS_H +#define AS_TEXTS_H + +// Compiler messages + +#define TXT_s_ALREADY_DECLARED "'%s' is already declared" +#define TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED "Abstract class '%s' cannot be instantiated" +#define TXT_ACCESSING_PRIVATE_PROP_s "Accessing private property '%s' of parent class" +#define TXT_ARG_NOT_LVALUE "Output argument expression is not assignable" +#define TXT_ATTR_s_INFORMED_MULTIPLE_TIMES "Attribute '%s' informed multiple times" +#define TXT_AUTO_NOT_ALLOWED "Auto is not allowed here" + +#define TXT_BOTH_MUST_BE_SAME "Both expressions must have the same type" +#define TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR "Both conditions must call constructor" +#define TEXT_BASE_DOESNT_HAVE_DEF_CONSTR "Base class doesn't have default constructor. Make explicit call to base constructor" + +#define TXT_CANDIDATES_ARE "Candidates are:" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS "Can't call a constructor in loops" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH "Can't call a constructor in switch" +#define TXT_CANNOT_CALL_CONSTRUCTOR_TWICE "Can't call a constructor multiple times" +#define TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES "Can't create delegate for types that do not support handles" +#define TXT_CANNOT_IMPLEMENT_SELF "Can't implement itself, or another interface that implements this interface" +#define TXT_CANNOT_INHERIT_FROM_s_FINAL "Can't inherit from class '%s' marked as final" +#define TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES "Can't inherit from multiple classes" +#define TXT_CANNOT_INHERIT_FROM_SELF "Can't inherit from itself, or another class that inherits from this class" +#define TXT_CANNOT_PASS_CLASS_METHOD_AS_ARG "Can't pass class method as arg directly. Use a delegate object instead" +#define TXT_CANNOT_RESOLVE_AUTO "Unable to resolve auto type" +#define TXT_CANNOT_RETURN_REF_TO_LOCAL "Can't return reference to local value." +#define TXT_CANT_CONSTRUCT_s_USE_REF_CAST "Can't construct handle '%s'. Use ref cast instead" +#define TXT_CANT_IMPLICITLY_CONVERT_s_TO_s "Can't implicitly convert from '%s' to '%s'." +#define TXT_CANT_RETURN_VALUE "Can't return value when return type is 'void'" +#define TXT_CHANGE_SIGN "Implicit conversion changed sign of value" +#define TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT "A class cannot be both abstract and final" +#define TXT_COMPILING_s "Compiling %s" +#define TXT_COMPOUND_ASGN_ON_VALUE_TYPE "Compound assignments with property accessors on value types are not supported" +#define TXT_COMPOUND_ASGN_WITH_IDX_PROP "Compound assignments with indexed property accessors are not supported" +#define TXT_COMPOUND_ASGN_REQUIRE_GET_SET "Compound assignments with property accessors require both get and set accessors" + +#define TXT_DATA_TYPE_CANT_BE_s "Data type can't be '%s'" +#define TXT_DECL_IN_SWITCH "Variables cannot be declared in switch cases, except inside statement blocks" +#define TXT_DEFAULT_MUST_BE_LAST "The default case must be the last one" +#define TXT_DEF_ARG_MISSING_IN_FUNC_s "All subsequent parameters after the first default value must have default values in function '%s'" +#define TXT_DEF_ARG_TYPE_DOESNT_MATCH "The type of the default argument expression doesn't match the function parameter type" +#define TXT_DUPLICATE_NAMED_ARG "Duplicate named argument" +#define TXT_DERIVED_METHOD_MUST_HAVE_SAME_RETTYPE_s "The method in the derived class must have the same return type as in the base class: '%s'" +#define TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM "The destructor must not have any parameters" +#define TXT_DESTRUCTOR_s_s_NAME_ERROR "The name of the destructor '%s::~%s' must be the same as the class" +#define TXT_DISALLOW_ASSIGN_ON_REF_TYPE "Value assignment on reference types is not allowed. Did you mean to do a handle assignment?" +#define TXT_DISALLOW_COMPOUND_ASSIGN_ON_REF_TYPE "Compound assignment on reference types is not allowed" +#define TXT_DUPLICATE_SWITCH_CASE "Duplicate switch case" + +#define TXT_ELSE_WITH_EMPTY_STATEMENT "Else with empty statement" +#define TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED "Empty list element is not allowed" +#define TXT_EMPTY_SWITCH "Empty switch statement" +#define TXT_EXPECTED_s "Expected '%s'" +#define TXT_EXPECTED_CONSTANT "Expected constant" +#define TXT_EXPECTED_DATA_TYPE "Expected data type" +#define TXT_EXPECTED_EXPRESSION_VALUE "Expected expression value" +#define TXT_EXPECTED_IDENTIFIER "Expected identifier" +#define TXT_EXPECTED_LIST "Expected a list enclosed by { } to match pattern" +#define TXT_EXPECTED_METHOD_OR_PROPERTY "Expected method or property" +#define TXT_EXPECTED_ONE_OF "Expected one of: " +#define TXT_EXPECTED_OPERATOR "Expected operator" +#define TXT_EXPECTED_s_OR_s "Expected '%s' or '%s'" +#define TXT_EXPECTED_POST_OPERATOR "Expected post operator" +#define TXT_EXPECTED_PRE_OPERATOR "Expected pre operator" +#define TXT_EXPECTED_STRING "Expected string" +#define TXT_EXPR_DOESNT_EVAL_TO_FUNC "Expression doesn't evaluate to a function" +#define TXT_EXPR_MUST_BE_BOOL "Expression must be of boolean type" + +#define TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s "Failed while compiling default arg for parameter %d in function '%s'" +#define TXT_FAILED_TO_CREATE_TEMP_OBJ "Previous error occurred while attempting to create a temporary copy of object" +#define TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC "Float value truncated in implicit conversion to integer" +#define TXT_FOUND_MULTIPLE_ENUM_VALUES "Found multiple matching enum values" +#define TXT_FUNCTION_ALREADY_EXIST "A function with the same name and parameters already exists" +#define TXT_FUNCTION_s_NOT_FOUND "Function '%s' not found" + +#define TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s "The property '%s' has mismatching types for the get and set accessors" +#define TXT_GLOBAL_VARS_NOT_ALLOWED "Global variables have been disabled by the application" + +#define TXT_HANDLE_ASSIGN_ON_NON_HANDLE_PROP "It is not allowed to perform a handle assignment on a non-handle property" +#define TXT_HANDLE_COMPARISON "The operand is implicitly converted to handle in order to compare them" +#define TXT_HANDLE_OF_HANDLE_IS_NOT_ALLOWED "Handle to handle is not allowed" +#define TXT_s_HIDES_VAR_IN_OUTER_SCOPE "Variable '%s' hides another variable of same name in outer scope" + +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE "Identifier '%s' is not a data type" +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS "Identifier '%s' is not a data type in global namespace" +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s "Identifier '%s' is not a data type in namespace '%s' or parent" +#define TXT_IF_WITH_EMPTY_STATEMENT "If with empty statement" +#define TXT_ILLEGAL_MEMBER_TYPE "Illegal member type" +// TODO: Should be TXT_ILLEGAL_OPERATION_ON_s +#define TXT_ILLEGAL_OPERATION "Illegal operation on this datatype" +#define TXT_ILLEGAL_OPERATION_ON_s "Illegal operation on '%s'" +#define TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST "Illegal target type for reference cast" +#define TXT_ILLEGAL_VARIABLE_NAME_s "Illegal variable name '%s'." +#define TXT_INHERITED_PRIVATE_PROP_ACCESS_s "Illegal access to inherited private property '%s'" +#define TXT_INIT_LIST_CANNOT_BE_USED_WITH_s "Initialization lists cannot be used with '%s'" +#define TXT_INSTANCING_INVLD_TMPL_TYPE_s_s "Attempting to instantiate invalid template type '%s<%s>'" +#define TXT_INSTEAD_FOUND_s "Instead found '%s'" +#define TXT_INSTEAD_FOUND_IDENTIFIER_s "Instead found identifier '%s'" +#define TXT_INSTEAD_FOUND_KEYWORD_s "Instead found reserved keyword '%s'" +#define TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED "Interface '%s' cannot be instantiated" +#define TXT_INTERFACE_CAN_ONLY_IMPLEMENT_INTERFACE "Interfaces can only implement other interfaces" +#define TXT_INVALID_BREAK "Invalid 'break'" +#define TXT_INVALID_CHAR_LITERAL "Invalid character literal" +#define TXT_INVALID_CONTINUE "Invalid 'continue'" +#define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence" +#define TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME "Invalid expression: ambiguous name" +#define TXT_INVALID_EXPRESSION_LAMBDA "Invalid expression: stand-alone anonymous function" +#define TXT_INVALID_OP_ON_METHOD "Invalid operation on method" +#define TXT_INVALID_REF_PROP_ACCESS "Invalid reference. Property accessors cannot be used in combined read/write operations" +#define TXT_INVALID_SCOPE "Invalid scope resolution" +#define TXT_INVALID_TYPE "Invalid type" +#define TXT_INVALID_UNICODE_FORMAT_EXPECTED_d "Invalid unicode escape sequence, expected %d hex digits" +#define TXT_INVALID_UNICODE_VALUE "Invalid unicode code point" +#define TXT_INVALID_UNICODE_SEQUENCE_IN_SRC "Invalid unicode sequence in source" + +#define TXT_METHOD_CANNOT_OVERRIDE_s "Method '%s' declared as final and cannot be overridden" +#define TXT_METHOD_CANT_HAVE_NAME_OF_CLASS "The method cannot be named with the class name" +#define TXT_METHOD_s_DOES_NOT_OVERRIDE "Method '%s' marked as override but does not replace any base class or interface method" +#define TXT_METHOD_s_s_HAS_NO_RETURN_TYPE "Method '%s::%s' is missing the return type, nor is it the same name as object to be a constructor" +#define TXT_MISSING_IMPLEMENTATION_OF_s "Missing implementation of '%s'" +#define TXT_MIXIN_CANNOT_BE_DECLARED_AS_s "Mixin class cannot be declared as '%s'" +#define TXT_MIXIN_CANNOT_HAVE_CONSTRUCTOR "Mixin classes cannot have constructors or destructors" +#define TXT_MIXIN_CLASS_CANNOT_INHERIT "Mixin class cannot inherit from classes" +#define TXT_MIXIN_CANNOT_HAVE_CHILD_TYPES "Mixin classes cannot have child types" +#define TXT_MORE_THAN_ONE_MATCHING_OP "Found more than one matching operator" +#define TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s "Multiple matching signatures to '%s'" +#define TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s "Found multiple get accessors for property '%s'" +#define TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s "Found multiple set accessors for property '%s'" +#define TXT_MULTILINE_STRINGS_NOT_ALLOWED "Multiline strings are not allowed in this application" +#define TXT_MUST_BE_OBJECT "Only objects have constructors" +#define TXT_MUST_RETURN_VALUE "Must return a value" + +#define TXT_NAMESPACE_s_DOESNT_EXIST "Namespace '%s' doesn't exist." +#define TXT_NAME_CONFLICT_s_EXTENDED_TYPE "Name conflict. '%s' is an extended data type." +#define TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY "Name conflict. '%s' is a global property." +#define TXT_NAME_CONFLICT_s_IS_NAMED_TYPE "Name conflict. '%s' is a named type." +#define TXT_NAME_CONFLICT_s_IS_FUNCDEF "Name conflict. '%s' is a funcdef." +#define TXT_NAME_CONFLICT_s_IS_MIXIN "Name conflict. '%s' is a mixin class." +#define TXT_NAME_CONFLICT_s_STRUCT "Name conflict. '%s' is a class." +#define TXT_NAME_CONFLICT_s_OBJ_PROPERTY "Name conflict. '%s' is an object property." +#define TXT_NAME_CONFLICT_s_METHOD "Name conflict. '%s' is a class method." +#define TXT_NAME_CONFLICT_s_ALREADY_USED "Name conflict. '%s' is already used." +#define TXT_NAMED_ARGS_WITH_OLD_SYNTAX "Detected named argument with old syntax" +#define TXT_NO_APPROPRIATE_INDEX_OPERATOR "No appropriate indexing operator found" +#define TXT_NO_APPROPRIATE_OPHNDLASSIGN_s "No appropriate opHndlAssign method found in '%s' for handle assignment" +#define TXT_NO_APPROPRIATE_OPEQUALS "No appropriate opEquals method found" +#define TXT_NO_CONVERSION_s_TO_s "No conversion from '%s' to '%s' available." +#define TXT_NO_CONVERSION_s_TO_MATH_TYPE "No conversion from '%s' to math type available." +#define TXT_NO_DEFAULT_ARRAY_TYPE "The application doesn't support the default array type." +#define TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s "No default constructor for object of type '%s'." +#define TXT_NO_DEFAULT_COPY_OP_FOR_s "No appropriate opAssign method found in '%s' for value assignment" +#define TXT_NO_COPY_CONSTRUCTOR_FOR_s "No copy constructor for object of type '%s'." +#define TXT_NO_MATCHING_SIGNATURES_TO_s "No matching signatures to '%s'" +#define TXT_NO_MATCHING_OP_FOUND_FOR_TYPE_s "No matching operator that takes the type '%s' found" +#define TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s "No matching operator that takes the types '%s' and '%s' found" +#define TXT_NON_CONST_METHOD_ON_CONST_OBJ "Non-const method call on read-only object reference" +#define TXT_NONTERMINATED_STRING "Non-terminated string literal" +#define TXT_NOT_A_FUNC_s_IS_VAR "Expression doesn't form a function call. '%s' is a variable of a non-function type" +#define TXT_NOT_ALL_PATHS_RETURN "Not all paths return a value" +#define TXT_NOT_ENOUGH_VALUES_FOR_LIST "Not enough values to match pattern" +#define TXT_s_NOT_DECLARED "'%s' is not declared" +#define TXT_NOT_EXACT "Implicit conversion of value is not exact" +#define TXT_s_NOT_INITIALIZED "'%s' is not initialized." +#define TXT_NOT_LVALUE "Expression is not an l-value" +#define TXT_s_NOT_MEMBER_OF_s "'%s' is not a member of '%s'" +#define TXT_NOT_VALID_REFERENCE "Not a valid reference" +#define TXT_NOT_VALID_LVALUE "Not a valid lvalue" +#define TXT_NOTHING_WAS_BUILT "Nothing was built in the module" + +#define TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP "Type '%s' doesn't support the indexing operator" +#define TXT_OBJECT_HANDLE_NOT_SUPPORTED "Object handle is not supported for this type" +#define TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT "Only object types that support object handles can use &inout. Use &in or &out instead" +#define TXT_ONLY_ONE_ARGUMENT_IN_CAST "A cast operator has one argument" +#define TXT_ONLY_ONE_FUNCTION_ALLOWED "The code must contain one and only one function" +#define TXT_ONLY_ONE_VARIABLE_ALLOWED "The code must contain one and only one global variable" +#define TXT_OPERANDS_MUST_BE_HANDLES "Both operands must be handles when comparing identity" +#define TXT_OVERLOAD_CONFLICTS_DUE_TO_DEFAULT_ARGS "The overloaded functions are identical on initial parameters without default arguments" + +#define TXT_PARAMETER_ALREADY_DECLARED "Parameter already declared" +#define TXT_PARAMETER_CANT_BE_s "Parameter type can't be '%s', because the type cannot be instantiated." +#define TXT_POS_ARG_AFTER_NAMED_ARG "Positional arguments cannot be passed after named arguments" +#define TXT_PRIVATE_METHOD_CALL_s "Illegal call to private method '%s'" +#define TXT_PRIVATE_PROP_ACCESS_s "Illegal access to private property '%s'" +#define TXT_PROTECTED_METHOD_CALL_s "Illegal call to protected method '%s'" +#define TXT_PROTECTED_PROP_ACCESS_s "Illegal access to protected property '%s'" +#define TXT_PROPERTY_ACCESSOR_DISABLED "Property accessors have been disabled by the application" +#define TXT_PROPERTY_ACCESSOR_MUST_BE_IMPLEMENTED "Property accessor must be implemented" +#define TXT_PROPERTY_CANT_BE_CONST "Class properties cannot be declared as const" +#define TXT_PROPERTY_HAS_NO_GET_ACCESSOR "The property has no get accessor" +#define TXT_PROPERTY_HAS_NO_SET_ACCESSOR "The property has no set accessor" +#define TXT_PROPERTY_WITHOUT_ACCESSOR "Virtual property must have at least one get or set accessor" + +#define TXT_REF_CANT_BE_TO_LOCAL_VAR "Resulting reference cannot be returned. Returned references must not refer to local variables." +#define TXT_REF_CANT_BE_RETURNED_DEFERRED_PARAM "Resulting reference cannot be returned. There are deferred arguments that may invalidate it." +#define TXT_REF_CANT_BE_RETURNED_LOCAL_VARS "Resulting reference cannot be returned. The expression uses objects that during cleanup may invalidate it." +#define TXT_REF_IS_READ_ONLY "Reference is read-only" +#define TXT_REF_IS_TEMP "Reference is temporary" +#define TXT_REF_TYPE_CANT_BE_PASSED_BY_VAL "Reference types cannot be passed by value in function parameters" +#define TXT_REF_TYPE_CANT_BE_RETURNED_BY_VAL "Reference types cannot be returned by value from functions" +#define TXT_RETURN_CANT_BE_s "Return type can't be '%s'" + +#define TXT_SHARED_CANNOT_ACCESS_NON_SHARED_VAR_s "Shared code cannot access non-shared global variable '%s'" +#define TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s "Shared code cannot call non-shared function '%s'" +#define TXT_SHARED_CANNOT_IMPLEMENT_NON_SHARED_s "Shared type cannot implement non-shared interface '%s'" +#define TXT_SHARED_CANNOT_INHERIT_FROM_NON_SHARED_s "Shared class cannot inherit from non-shared class '%s'" +#define TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s "Shared code cannot use non-shared type '%s'" +#define TXT_SHARED_s_DOESNT_MATCH_ORIGINAL "Shared type '%s' doesn't match the original declaration in other module" +#define TXT_SECTION_IS_EMPTY "The script section is empty" +#define TXT_SIGNED_UNSIGNED_MISMATCH "Signed/Unsigned mismatch" +#define TXT_STRINGS_NOT_RECOGNIZED "Strings are not recognized by the application" +#define TXT_SWITCH_CASE_MUST_BE_CONSTANT "Case expressions must be constants" +#define TXT_SWITCH_MUST_BE_INTEGRAL "Switch expressions must be integral numbers" + +#define TXT_TMPL_s_EXPECTS_d_SUBTYPES "Template '%s' expects %d sub type(s)" +#define TXT_TMPL_SUBTYPE_MUST_NOT_BE_READ_ONLY "Template subtype must not be read-only" +#define TXT_TOO_MANY_JUMP_LABELS "The function has too many jump labels to handle. Split the function into smaller ones." +#define TXT_TOO_MANY_VALUES_FOR_LIST "Too many values to match pattern" +#define TXT_TYPE_s_CANNOT_BE_REFERENCE "Type '%s' cannot be a reference" +#define TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE "Type '%s' is not available for this module" +#define TXT_TYPE_s_NOT_TEMPLATE "Type '%s' is not a template type" + +#define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" +#define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" +#define TXT_UNEXPECTED_VAR_DECL "Unexpected variable declaration" +#define TXT_UNINITIALIZED_GLOBAL_VAR_s "Use of uninitialized global variable '%s'." +#define TXT_UNKNOWN_SCOPE_s "Unknown scope '%s'" +#define TXT_UNREACHABLE_CODE "Unreachable code" +#define TXT_UNRECOGNIZED_VIRTUAL_PROPERTY_NODE "Virtual property contains unrecognized aspect" +#define TXT_UNUSED_SCRIPT_NODE "Unused script node" + +#define TXT_VALUE_TOO_LARGE_FOR_TYPE "Value is too large for data type" +#define TXT_VOID_CANT_BE_OPERAND "Void cannot be an operand in expressions" + +#define TXT_WARNINGS_TREATED_AS_ERROR "Warnings are treated as errors by the application" +#define TXT_WHILE_PARSING_ARG_LIST "While parsing argument list" +#define TXT_WHILE_PARSING_EXPRESSION "While parsing expression" +#define TXT_WHILE_PARSING_INIT_LIST "While parsing initialization list" +#define TXT_WHILE_PARSING_NAMESPACE "While parsing namespace" +#define TXT_WHILE_PARSING_STATEMENT_BLOCK "While parsing statement block" +#define TXT_WHILE_INCLUDING_MIXIN "Previous error occurred while including mixin" + +// Global variable initialization + +#define TXT_FAILED_TO_INITIALIZE_s "Failed to initialize global variable '%s'" +#define TXT_EXCEPTION_s_IN_s "Exception '%s' in '%s'" + +// Engine message + +#define TXT_AUTOHANDLE_CANNOT_BE_USED_FOR_NOCOUNT "Autohandles cannot be used with types that have been registered with NOCOUNT" +#define TXT_FIRST_PARAM_MUST_BE_REF_FOR_TEMPLATE_FACTORY "First parameter to template factory must be a reference. This will be used to pass the object type of the template" +#define TXT_INVALID_CONFIGURATION "Invalid configuration. Verify the registered application interface." +#define TXT_VALUE_TYPE_MUST_HAVE_SIZE "A value type must be registered with a non-zero size" +#define TXT_TYPE_s_IS_MISSING_BEHAVIOURS "Type '%s' is missing behaviours" +#define TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE "The behaviour is not compatible with the type" +#define TXT_GC_REQUIRE_ADD_REL_GC_BEHAVIOUR "A garbage collected type must have the addref, release, and all gc behaviours" +#define TXT_SCOPE_REQUIRE_REL_BEHAVIOUR "A scoped reference type must have the release behaviour" +#define TXT_REF_REQUIRE_ADD_REL_BEHAVIOUR "A reference type must have the addref and release behaviours" +#define TXT_NON_POD_REQUIRE_CONSTR_DESTR_BEHAVIOUR "A non-pod value type must have at least one constructor and the destructor behaviours" +#define TXT_CANNOT_PASS_TYPE_s_BY_VAL "Can't pass type '%s' by value unless the application type is informed in the registration" +#define TXT_CANNOT_RET_TYPE_s_BY_VAL "Can't return type '%s' by value unless the application type is informed in the registration" +// TODO: Should be something like "This platform requires that AngelScript knows the exact content of the type '%s' in order to pass by value to application in native calling convention" +#define TXT_DONT_SUPPORT_TYPE_s_BY_VAL "Don't support passing type '%s' by value to application in native calling convention on this platform" +// TODO: Should be something like "This platform requires that AngelScript knows the exact content of the type '%s' in order to return by value from application in native calling convention" +#define TXT_DONT_SUPPORT_RET_TYPE_s_BY_VAL "Don't support returning type '%s' by value from application in native calling convention on this platform" +#define TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s "Object {%d}. GC cannot destroy an object of type '%s' as it doesn't know how many references to there are." +#define TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d "Object {%d}. GC cannot destroy an object of type '%s' as it can't see all references. Current ref count is %d." +#define TXT_OBJECT_TYPE_s_DOESNT_EXIST "Object type '%s' doesn't exist" +#define TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER "Cannot register. The template type instance '%s' has already been generated." +#define TXT_TEMPLATE_TYPE_s_DOESNT_EXIST "Template type '%s' doesn't exist" +#define TXT_TEMPLATE_SUBTYPE_s_DOESNT_EXIST "Template subtype '%s' doesn't exist" +#define TXT_TEMPLATE_LIST_FACTORY_EXPECTS_2_REF_PARAMS "Template list factory expects two reference parameters. The last is the pointer to the initialization buffer" +#define TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM "List factory expects only one reference parameter. The pointer to the initialization buffer will be passed in this parameter" +#define TXT_FAILED_READ_SUBTYPE_OF_TEMPLATE_s "Failed to read subtype of template type '%s'" +#define TXT_FAILED_IN_FUNC_s_d "Failed in call to function '%s' (Code: %d)" +#define TXT_FAILED_IN_FUNC_s_WITH_s_d "Failed in call to function '%s' with '%s' (Code: %d)" +#define TXT_FAILED_IN_FUNC_s_WITH_s_AND_s_d "Failed in call to function '%s' with '%s' and '%s' (Code: %d)" +#define TXT_GC_RECEIVED_NULL_PTR "AddScriptObjectToGC called with null pointer" +#define TXT_EXCEPTION_IN_NESTED_CALL "An exception occurred in a nested call" +#define TXT_TYPE_s_IS_STILL_USED_BY_FUNC_s "Type '%s' is still used by function '%s'" +#define TXT_PREV_TYPE_IS_NAMED_s "The builtin type in previous message is named '%s'" +#define TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d "The function in previous message is named '%s'. The func type is %d" +#define TXT_RESURRECTING_SCRIPTOBJECT_s "The script object of type '%s' is being resurrected illegally during destruction" +#define TXT_INVALID_BYTECODE_d "LoadByteCode failed. The bytecode is invalid. Number of bytes read from stream: %d" +#define TXT_NO_JIT_IN_FUNC_s "Function '%s' appears to have been compiled without JIT entry points" +#define TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN "Uh oh! The engine's reference count is increasing while it is being destroyed. Make sure references needed for clean-up are immediately released" +#define TXT_MODULE_IS_IN_USE "The module is still in use and cannot be rebuilt. Discard it and request another module" +#define TXT_EXTRNL_REF_TO_MODULE_s "There is an external reference to an object in module '%s', preventing it from being deleted" + +// Internal names + +#define TXT_PROPERTY "Property" +#define TXT_SYSTEM_FUNCTION "System function" +#define TXT_VARIABLE_DECL "Variable declaration" + +// Exceptions + +#define TXT_STACK_OVERFLOW "Stack overflow" +#define TXT_NULL_POINTER_ACCESS "Null pointer access" +#define TXT_DIVIDE_BY_ZERO "Divide by zero" +#define TXT_DIVIDE_OVERFLOW "Overflow in integer division" +#define TXT_POW_OVERFLOW "Overflow in exponent operation" +#define TXT_UNRECOGNIZED_BYTE_CODE "Unrecognized byte code" +#define TXT_INVALID_CALLING_CONVENTION "Invalid calling convention" +#define TXT_UNBOUND_FUNCTION "Unbound function called" +#define TXT_OUT_OF_BOUNDS "Out of range" +#define TXT_EXCEPTION_CAUGHT "Caught an exception from the application" +#define TXT_MISMATCH_IN_VALUE_ASSIGN "Mismatching types in value assignment" + +#endif diff --git a/3rdparty/angelscript/include/as_thread.h b/3rdparty/angelscript/include/as_thread.h new file mode 100644 index 0000000..f4abf88 --- /dev/null +++ b/3rdparty/angelscript/include/as_thread.h @@ -0,0 +1,108 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_thread.h +// +// Classes for multi threading support +// + +#ifndef AS_THREAD_H +#define AS_THREAD_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_map.h" +#include "as_criticalsection.h" + +BEGIN_AS_NAMESPACE + +class asCThreadLocalData; + +class asCThreadManager : public asIThreadManager +{ +public: + static asCThreadLocalData *GetLocalData(); + static int CleanupLocalData(); + + static int Prepare(asIThreadManager *externalThreadMgr); + static void Unprepare(); + + // This read/write lock can be used by the application to provide simple synchronization + DECLAREREADWRITELOCK(appRWLock) + +protected: + asCThreadManager(); + ~asCThreadManager(); + + // No need to use the atomic int here, as it will only be + // updated within the thread manager's critical section + int refCount; + +#ifndef AS_NO_THREADS +#if defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + // On Windows Store we must use MSVC specific thread variables for thread + // local storage, as the TLS API isn't available. On desktop we can't use + // this as it may cause problems if the library is used in a dll. + // ref: http://msdn.microsoft.com/en-us/library/2s9wt68x.aspx + // ref: http://msdn.microsoft.com/en-us/library/9w1sdazb.aspx + __declspec(thread) static asCThreadLocalData *tld; +#else + asDWORD tlsKey; +#endif + DECLARECRITICALSECTION(criticalSection); +#else + asCThreadLocalData *tld; +#endif +}; + +//====================================================================== + +class asIScriptContext; + +class asCThreadLocalData +{ +public: + asCArray activeContexts; + asCString string; + +protected: + friend class asCThreadManager; + + asCThreadLocalData(); + ~asCThreadLocalData(); +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_tokendef.h b/3rdparty/angelscript/include/as_tokendef.h new file mode 100644 index 0000000..ac8bf1a --- /dev/null +++ b/3rdparty/angelscript/include/as_tokendef.h @@ -0,0 +1,319 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokendef.h +// +// Definitions for tokens identifiable by the tokenizer +// + + +#ifndef AS_TOKENDEF_H +#define AS_TOKENDEF_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +enum eTokenType +{ + ttUnrecognizedToken, + + ttEnd, // End of file + + // White space and comments + ttWhiteSpace, // ' ', '\t', '\r', '\n', UTF8 byte-order-mark + ttOnelineComment, // // \n + ttMultilineComment, // /* */ + + // Atoms + ttIdentifier, // abc123 + ttIntConstant, // 1234 + ttFloatConstant, // 12.34e56f + ttDoubleConstant, // 12.34e56 + ttStringConstant, // "123" + ttMultilineStringConstant, // + ttHeredocStringConstant, // """text""" + ttNonTerminatedStringConstant, // "123 + ttBitsConstant, // 0xFFFF + + // Math operators + ttPlus, // + + ttMinus, // - + ttStar, // * + ttSlash, // / + ttPercent, // % + ttStarStar, // ** + + ttHandle, // @ + + ttAddAssign, // += + ttSubAssign, // -= + ttMulAssign, // *= + ttDivAssign, // /= + ttModAssign, // %= + ttPowAssign, // **= + + ttOrAssign, // |= + ttAndAssign, // &= + ttXorAssign, // ^= + ttShiftLeftAssign, // <<= + ttShiftRightLAssign, // >>= + ttShiftRightAAssign, // >>>= + + ttInc, // ++ + ttDec, // -- + + ttDot, // . + ttScope, // :: + + // Statement tokens + ttAssignment, // = + ttEndStatement, // ; + ttListSeparator, // , + ttStartStatementBlock, // { + ttEndStatementBlock, // } + ttOpenParanthesis, // ( + ttCloseParanthesis, // ) + ttOpenBracket, // [ + ttCloseBracket, // ] + ttAmp, // & + + // Bitwise operators + ttBitOr, // | + ttBitNot, // ~ + ttBitXor, // ^ + ttBitShiftLeft, // << + ttBitShiftRight, // >> // TODO: In Java this is the arithmetical shift + ttBitShiftRightArith, // >>> // TODO: In Java this is the logical shift + + // Compare operators + ttEqual, // == + ttNotEqual, // != + ttLessThan, // < + ttGreaterThan, // > + ttLessThanOrEqual, // <= + ttGreaterThanOrEqual, // >= + + ttQuestion, // ? + ttColon, // : + + // Reserved keywords + ttIf, // if + ttElse, // else + ttFor, // for + ttWhile, // while + ttBool, // bool + ttFuncDef, // funcdef + ttImport, // import + ttInt, // int + ttInt8, // int8 + ttInt16, // int16 + ttInt64, // int64 + ttInterface, // interface + ttIs, // is + ttNotIs, // !is + ttUInt, // uint + ttUInt8, // uint8 + ttUInt16, // uint16 + ttUInt64, // uint64 + ttFloat, // float + ttVoid, // void + ttTrue, // true + ttFalse, // false + ttReturn, // return + ttNot, // not + ttAnd, // and, && + ttOr, // or, || + ttXor, // xor, ^^ + ttBreak, // break + ttContinue, // continue + ttConst, // const + ttDo, // do + ttDouble, // double + ttSwitch, // switch + ttCase, // case + ttDefault, // default + ttIn, // in + ttOut, // out + ttInOut, // inout + ttNull, // null + ttClass, // class + ttTypedef, // typedef + ttEnum, // enum + ttCast, // cast + ttPrivate, // private + ttProtected, // protected + ttNamespace, // namespace + ttMixin, // mixin + ttAuto // auto +}; + +struct sTokenWord +{ + const char *word; + size_t wordLength; + eTokenType tokenType; +}; + +#define asTokenDef(str, tok) {str, sizeof(str)-1, tok} + +sTokenWord const tokenWords[] = +{ + asTokenDef("+" , ttPlus), + asTokenDef("+=" , ttAddAssign), + asTokenDef("++" , ttInc), + asTokenDef("-" , ttMinus), + asTokenDef("-=" , ttSubAssign), + asTokenDef("--" , ttDec), + asTokenDef("*" , ttStar), + asTokenDef("*=" , ttMulAssign), + asTokenDef("/" , ttSlash), + asTokenDef("/=" , ttDivAssign), + asTokenDef("%" , ttPercent), + asTokenDef("%=" , ttModAssign), + asTokenDef("**" , ttStarStar), + asTokenDef("**=" , ttPowAssign), + asTokenDef("=" , ttAssignment), + asTokenDef("==" , ttEqual), + asTokenDef("." , ttDot), + asTokenDef("|" , ttBitOr), + asTokenDef("|=" , ttOrAssign), + asTokenDef("||" , ttOr), + asTokenDef("&" , ttAmp), + asTokenDef("&=" , ttAndAssign), + asTokenDef("&&" , ttAnd), + asTokenDef("^" , ttBitXor), + asTokenDef("^=" , ttXorAssign), + asTokenDef("^^" , ttXor), + asTokenDef("<" , ttLessThan), + asTokenDef("<=" , ttLessThanOrEqual), + asTokenDef("<<" , ttBitShiftLeft), + asTokenDef("<<=" , ttShiftLeftAssign), + asTokenDef(">" , ttGreaterThan), + asTokenDef(">=" , ttGreaterThanOrEqual), + asTokenDef(">>" , ttBitShiftRight), + asTokenDef(">>=" , ttShiftRightLAssign), + asTokenDef(">>>" , ttBitShiftRightArith), + asTokenDef(">>>=" , ttShiftRightAAssign), + asTokenDef("~" , ttBitNot), + asTokenDef(";" , ttEndStatement), + asTokenDef("," , ttListSeparator), + asTokenDef("{" , ttStartStatementBlock), + asTokenDef("}" , ttEndStatementBlock), + asTokenDef("(" , ttOpenParanthesis), + asTokenDef(")" , ttCloseParanthesis), + asTokenDef("[" , ttOpenBracket), + asTokenDef("]" , ttCloseBracket), + asTokenDef("?" , ttQuestion), + asTokenDef(":" , ttColon), + asTokenDef("::" , ttScope), + asTokenDef("!" , ttNot), + asTokenDef("!=" , ttNotEqual), + asTokenDef("!is" , ttNotIs), + asTokenDef("@" , ttHandle), + asTokenDef("and" , ttAnd), + asTokenDef("auto" , ttAuto), + asTokenDef("bool" , ttBool), + asTokenDef("break" , ttBreak), + asTokenDef("case" , ttCase), + asTokenDef("cast" , ttCast), + asTokenDef("class" , ttClass), + asTokenDef("const" , ttConst), + asTokenDef("continue" , ttContinue), + asTokenDef("default" , ttDefault), + asTokenDef("do" , ttDo), +#ifdef AS_USE_DOUBLE_AS_FLOAT + asTokenDef("double" , ttFloat), +#else + asTokenDef("double" , ttDouble), +#endif + asTokenDef("else" , ttElse), + asTokenDef("enum" , ttEnum), + asTokenDef("false" , ttFalse), + asTokenDef("float" , ttFloat), + asTokenDef("for" , ttFor), + asTokenDef("funcdef" , ttFuncDef), + asTokenDef("if" , ttIf), + asTokenDef("import" , ttImport), + asTokenDef("in" , ttIn), + asTokenDef("inout" , ttInOut), + asTokenDef("int" , ttInt), + asTokenDef("int8" , ttInt8), + asTokenDef("int16" , ttInt16), + asTokenDef("int32" , ttInt), + asTokenDef("int64" , ttInt64), + asTokenDef("interface" , ttInterface), + asTokenDef("is" , ttIs), + asTokenDef("mixin" , ttMixin), + asTokenDef("namespace" , ttNamespace), + asTokenDef("not" , ttNot), + asTokenDef("null" , ttNull), + asTokenDef("or" , ttOr), + asTokenDef("out" , ttOut), + asTokenDef("private" , ttPrivate), + asTokenDef("protected" , ttProtected), + asTokenDef("return" , ttReturn), + asTokenDef("switch" , ttSwitch), + asTokenDef("true" , ttTrue), + asTokenDef("typedef" , ttTypedef), + asTokenDef("uint" , ttUInt), + asTokenDef("uint8" , ttUInt8), + asTokenDef("uint16" , ttUInt16), + asTokenDef("uint32" , ttUInt), + asTokenDef("uint64" , ttUInt64), + asTokenDef("void" , ttVoid), + asTokenDef("while" , ttWhile), + asTokenDef("xor" , ttXor), +}; + +const unsigned int numTokenWords = sizeof(tokenWords)/sizeof(sTokenWord); + +const char * const whiteSpace = " \t\r\n"; + +// Some keywords that are not considered tokens by the parser +// These only have meaning in specific situations. Outside these +// situations they are treated as normal identifiers. +const char * const THIS_TOKEN = "this"; +const char * const FROM_TOKEN = "from"; +const char * const SUPER_TOKEN = "super"; +const char * const SHARED_TOKEN = "shared"; +const char * const FINAL_TOKEN = "final"; +const char * const OVERRIDE_TOKEN = "override"; +const char * const GET_TOKEN = "get"; +const char * const SET_TOKEN = "set"; +const char * const ABSTRACT_TOKEN = "abstract"; +const char * const FUNCTION_TOKEN = "function"; +const char * const IF_HANDLE_TOKEN = "if_handle_then_const"; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_tokenizer.h b/3rdparty/angelscript/include/as_tokenizer.h new file mode 100644 index 0000000..38a08c1 --- /dev/null +++ b/3rdparty/angelscript/include/as_tokenizer.h @@ -0,0 +1,79 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2013 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokenizer.cpp +// +// This class identifies tokens from the script code +// + + + +#ifndef AS_TOKENIZER_H +#define AS_TOKENIZER_H + +#include "as_config.h" +#include "as_tokendef.h" +#include "as_map.h" +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +class asCTokenizer +{ +public: + eTokenType GetToken(const char *source, size_t sourceLength, size_t *tokenLength, asETokenClass *tc = 0) const; + + static const char *GetDefinition(int tokenType); + +protected: + friend class asCScriptEngine; + + asCTokenizer(); + ~asCTokenizer(); + + asETokenClass ParseToken(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsWhiteSpace(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsComment(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsConstant(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsKeyWord(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsIdentifier(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const; + bool IsDigitInRadix(char ch, int radix) const; + + const asCScriptEngine *engine; + + const sTokenWord **keywordTable[256]; +}; + +END_AS_NAMESPACE + +#endif + diff --git a/3rdparty/angelscript/include/as_typeinfo.h b/3rdparty/angelscript/include/as_typeinfo.h new file mode 100644 index 0000000..fa8c2cf --- /dev/null +++ b/3rdparty/angelscript/include/as_typeinfo.h @@ -0,0 +1,241 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_typeinfo.h +// + + + +#ifndef AS_TYPEINFO_H +#define AS_TYPEINFO_H + +#include "as_config.h" +#include "as_string.h" +#include "as_atomic.h" +#include "as_datatype.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCModule; +class asCObjectType; +class asCEnumType; +class asCTypedefType; +class asCFuncdefType; +struct asSNameSpace; + +// TODO: type: asCPrimitiveType shall be implemented to represent primitives (void, int, double, etc) + +// TODO: type: asCTypeInfo should have an internal virtual method GetBehaviours. For asCObjectType it +// should return the beh member. For asCFuncdefType it should return the beh member of +// engine->functionBehaviours. This will allow the code that needs the behaviour to handle +// both object types and funcdefs the same way + +class asCTypeInfo : public asITypeInfo +{ +public: + //===================================== + // From asITypeInfo + //===================================== + asIScriptEngine *GetEngine() const; + const char *GetConfigGroup() const; + asDWORD GetAccessMask() const; + asIScriptModule *GetModule() const; + + // Memory management + int AddRef() const; + int Release() const; + + // Type info + const char *GetName() const; + const char *GetNamespace() const; + asITypeInfo *GetBaseType() const { return 0; } + bool DerivesFrom(const asITypeInfo *objType) const { UNUSED_VAR(objType); return 0; } + asDWORD GetFlags() const; + asUINT GetSize() const; + int GetTypeId() const; + int GetSubTypeId(asUINT subtypeIndex = 0) const { UNUSED_VAR(subtypeIndex); return -1; } + asITypeInfo *GetSubType(asUINT subtypeIndex = 0) const { UNUSED_VAR(subtypeIndex); return 0; } + asUINT GetSubTypeCount() const { return 0; } + + // Interfaces + asUINT GetInterfaceCount() const { return 0; } + asITypeInfo *GetInterface(asUINT index) const { UNUSED_VAR(index); return 0; } + bool Implements(const asITypeInfo *objType) const { UNUSED_VAR(objType); return false; } + + // Factories + asUINT GetFactoryCount() const { return 0; } + asIScriptFunction *GetFactoryByIndex(asUINT index) const { UNUSED_VAR(index); return 0; } + asIScriptFunction *GetFactoryByDecl(const char *decl) const { UNUSED_VAR(decl); return 0; } + + // Methods + asUINT GetMethodCount() const { return 0; } + asIScriptFunction *GetMethodByIndex(asUINT index, bool getVirtual) const { UNUSED_VAR(index); UNUSED_VAR(getVirtual); return 0; } + asIScriptFunction *GetMethodByName(const char *in_name, bool getVirtual) const { UNUSED_VAR(in_name); UNUSED_VAR(getVirtual); return 0; } + asIScriptFunction *GetMethodByDecl(const char *decl, bool getVirtual) const { UNUSED_VAR(decl); UNUSED_VAR(getVirtual); return 0; } + + // Properties + asUINT GetPropertyCount() const { return 0; } + int GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, bool *isProtected, int *offset, bool *isReference, asDWORD *accessMask) const; + const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const { UNUSED_VAR(index); UNUSED_VAR(includeNamespace); return 0; } + + // Behaviours + asUINT GetBehaviourCount() const { return 0; } + asIScriptFunction *GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const { UNUSED_VAR(index); UNUSED_VAR(outBehaviour); return 0; } + + // Child types + asUINT GetChildFuncdefCount() const { return 0; } + asITypeInfo *GetChildFuncdef(asUINT index) const { UNUSED_VAR(index); return 0; } + asITypeInfo *GetParentType() const { return 0; } + + // Enums + virtual asUINT GetEnumValueCount() const { return 0; } + virtual const char *GetEnumValueByIndex(asUINT index, int *outValue) const { UNUSED_VAR(index); if (outValue) *outValue = 0; return 0; } + + // Typedef + virtual int GetTypedefTypeId() const { return asERROR; } + + // Funcdef + virtual asIScriptFunction *GetFuncdefSignature() const { return 0; } + + // User data + void *SetUserData(void *data, asPWORD type); + void *GetUserData(asPWORD type) const; + + //=========================================== + // Internal + //=========================================== +public: + asCTypeInfo(asCScriptEngine *engine); + virtual ~asCTypeInfo(); + + // Keep an internal reference counter to separate references coming from + // application or script objects and references coming from the script code + virtual int AddRefInternal(); + virtual int ReleaseInternal(); + + virtual void DestroyInternal() {} + + void CleanUserData(); + + bool IsShared() const; + + // These can be safely used on null pointers (which will return null) + friend asCObjectType *CastToObjectType(asCTypeInfo *); + friend asCEnumType *CastToEnumType(asCTypeInfo *); + friend asCTypedefType *CastToTypedefType(asCTypeInfo *); + friend asCFuncdefType *CastToFuncdefType(asCTypeInfo *); + + + asCString name; + asSNameSpace *nameSpace; + int size; + mutable int typeId; + asDWORD flags; + asDWORD accessMask; + + // Store the script section where the code was declared + int scriptSectionIdx; + // Store the location where the function was declared (row in the lower 20 bits, and column in the upper 12) + int declaredAt; + + asCScriptEngine *engine; + asCModule *module; + asCArray userData; + +protected: + friend class asCScriptEngine; + friend class asCConfigGroup; + friend class asCModule; + asCTypeInfo(); + + mutable asCAtomic externalRefCount; + asCAtomic internalRefCount; +}; + +struct asSEnumValue +{ + asCString name; + int value; +}; + +class asCEnumType : public asCTypeInfo +{ +public: + asCEnumType(asCScriptEngine *engine) : asCTypeInfo(engine) {} + ~asCEnumType(); + + asCArray enumValues; + + asUINT GetEnumValueCount() const; + const char *GetEnumValueByIndex(asUINT index, int *outValue) const; + +protected: + asCEnumType() : asCTypeInfo() {} +}; + +class asCTypedefType : public asCTypeInfo +{ +public: + asCTypedefType(asCScriptEngine *engine) : asCTypeInfo(engine) {} + ~asCTypedefType(); + + void DestroyInternal(); + + asCDataType aliasForType; // increase refCount for typeinfo inside datatype + + int GetTypedefTypeId() const; + +protected: + asCTypedefType() : asCTypeInfo() {} +}; + +class asCFuncdefType : public asCTypeInfo +{ +public: + asCFuncdefType(asCScriptEngine *engine, asCScriptFunction *func); + ~asCFuncdefType(); + + asIScriptFunction *GetFuncdefSignature() const; + asITypeInfo *GetParentType() const; + + void DestroyInternal(); + asCScriptFunction *funcdef; // increases refCount + asCObjectType *parentClass; // doesn't increase refCount + +protected: + asCFuncdefType() : asCTypeInfo(), funcdef(0), parentClass(0) {} +}; + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/as_variablescope.h b/3rdparty/angelscript/include/as_variablescope.h new file mode 100644 index 0000000..d4b220a --- /dev/null +++ b/3rdparty/angelscript/include/as_variablescope.h @@ -0,0 +1,87 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2012 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_variablescope.h +// +// A manager class for variable declarations +// + + +#ifndef AS_VARIABLESCOPE_H +#define AS_VARIABLESCOPE_H + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_array.h" +#include "as_string.h" +#include "as_datatype.h" + +BEGIN_AS_NAMESPACE + +struct sVariable +{ + asCString name; + asCDataType type; + int stackOffset; + bool isInitialized; + bool isPureConstant; + asQWORD constantValue; + bool onHeap; +}; + +class asCVariableScope +{ +public: + asCVariableScope(asCVariableScope *parent); + ~asCVariableScope(); + + void Reset(); + + int DeclareVariable(const char *name, const asCDataType &type, int stackOffset, bool isObjectOnHeap); + sVariable *GetVariable(const char *name); + sVariable *GetVariableByOffset(int offset); + + asCVariableScope *parent; + + bool isBreakScope; + bool isContinueScope; + + asCArray variables; +}; + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + +#endif diff --git a/3rdparty/angelscript/include/scriptarray.h b/3rdparty/angelscript/include/scriptarray.h new file mode 100644 index 0000000..e95b9a2 --- /dev/null +++ b/3rdparty/angelscript/include/scriptarray.h @@ -0,0 +1,136 @@ +#ifndef SCRIPTARRAY_H +#define SCRIPTARRAY_H + +#ifndef ANGELSCRIPT_H +// Avoid having to inform include path if header is already include before +#include +#endif + +// Sometimes it may be desired to use the same method names as used by C++ STL. +// This may for example reduce time when converting code from script to C++ or +// back. +// +// 0 = off +// 1 = on + +#ifndef AS_USE_STLNAMES +#define AS_USE_STLNAMES 0 +#endif + +BEGIN_AS_NAMESPACE + +struct SArrayBuffer; +struct SArrayCache; + +class CScriptArray +{ +public: + // Set the memory functions that should be used by all CScriptArrays + static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); + + // Factory functions + static CScriptArray *Create(asITypeInfo *ot); + static CScriptArray *Create(asITypeInfo *ot, asUINT length); + static CScriptArray *Create(asITypeInfo *ot, asUINT length, void *defaultValue); + static CScriptArray *Create(asITypeInfo *ot, void *listBuffer); + + // Memory management + void AddRef() const; + void Release() const; + + // Type information + asITypeInfo *GetArrayObjectType() const; + int GetArrayTypeId() const; + int GetElementTypeId() const; + + // Get the current size + asUINT GetSize() const; + + // Returns true if the array is empty + bool IsEmpty() const; + + // Pre-allocates memory for elements + void Reserve(asUINT maxElements); + + // Resize the array + void Resize(asUINT numElements); + + // Get a pointer to an element. Returns 0 if out of bounds + void *At(asUINT index); + const void *At(asUINT index) const; + + // Set value of an element. + // The value arg should be a pointer to the value that will be copied to the element. + // Remember, if the array holds handles the value parameter should be the + // address of the handle. The refCount of the object will also be incremented + void SetValue(asUINT index, void *value); + + // Copy the contents of one array to another (only if the types are the same) + CScriptArray &operator=(const CScriptArray&); + + // Compare two arrays + bool operator==(const CScriptArray &) const; + + // Array manipulation + void InsertAt(asUINT index, void *value); + void InsertAt(asUINT index, const CScriptArray &arr); + void InsertLast(void *value); + void RemoveAt(asUINT index); + void RemoveLast(); + void RemoveRange(asUINT start, asUINT count); + void SortAsc(); + void SortDesc(); + void SortAsc(asUINT startAt, asUINT count); + void SortDesc(asUINT startAt, asUINT count); + void Sort(asUINT startAt, asUINT count, bool asc); + void Reverse(); + int Find(void *value) const; + int Find(asUINT startAt, void *value) const; + int FindByRef(void *ref) const; + int FindByRef(asUINT startAt, void *ref) const; + + // Return the address of internal buffer for direct manipulation of elements + void *GetBuffer(); + + // GC methods + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +protected: + mutable int refCount; + mutable bool gcFlag; + asITypeInfo *objType; + SArrayBuffer *buffer; + int elementSize; + int subTypeId; + + // Constructors + CScriptArray(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list + CScriptArray(asUINT length, asITypeInfo *ot); + CScriptArray(asUINT length, void *defVal, asITypeInfo *ot); + CScriptArray(const CScriptArray &other); + virtual ~CScriptArray(); + + bool Less(const void *a, const void *b, bool asc, asIScriptContext *ctx, SArrayCache *cache); + void *GetArrayItemPointer(int index); + void *GetDataPointer(void *buffer); + void Copy(void *dst, void *src); + void Precache(); + bool CheckMaxSize(asUINT numElements); + void Resize(int delta, asUINT at); + void CreateBuffer(SArrayBuffer **buf, asUINT numElements); + void DeleteBuffer(SArrayBuffer *buf); + void CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src); + void Construct(SArrayBuffer *buf, asUINT start, asUINT end); + void Destruct(SArrayBuffer *buf, asUINT start, asUINT end); + bool Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const; +}; + +void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray); + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/include/scriptstdstring.h b/3rdparty/angelscript/include/scriptstdstring.h new file mode 100644 index 0000000..11181a0 --- /dev/null +++ b/3rdparty/angelscript/include/scriptstdstring.h @@ -0,0 +1,53 @@ +// +// Script std::string +// +// This function registers the std::string type with AngelScript to be used as the default string type. +// +// The string type is registered as a value type, thus may have performance issues if a lot of +// string operations are performed in the script. However, for relatively few operations, this should +// not cause any problem for most applications. +// + +#ifndef SCRIPTSTDSTRING_H +#define SCRIPTSTDSTRING_H + +#ifndef ANGELSCRIPT_H +// Avoid having to inform include path if header is already include before +#include +#endif + +#include + +//--------------------------- +// Compilation settings +// + +// The use of the string pool can improve performance quite drastically +// for scripts that work with a lot of literal string constants. +// +// 1 = on +// 0 = off + +#ifndef AS_USE_STRINGPOOL +#define AS_USE_STRINGPOOL 1 +#endif + +// Sometimes it may be desired to use the same method names as used by C++ STL. +// This may for example reduce time when converting code from script to C++ or +// back. +// +// 0 = off +// 1 = on + +#ifndef AS_USE_STLNAMES +#define AS_USE_STLNAMES 0 +#endif + +BEGIN_AS_NAMESPACE + +void RegisterStdString(asIScriptEngine *engine); +void RegisterStdStringUtils(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/3rdparty/angelscript/src/as_atomic.cpp b/3rdparty/angelscript/src/as_atomic.cpp new file mode 100644 index 0000000..e110b3f --- /dev/null +++ b/3rdparty/angelscript/src/as_atomic.cpp @@ -0,0 +1,179 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// +// as_atomic.cpp +// +// The implementation of the atomic class for thread safe reference counting +// + +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +asCAtomic::asCAtomic() +{ + value = 0; +} + +asDWORD asCAtomic::get() const +{ + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + + return value; +} + +void asCAtomic::set(asDWORD val) +{ + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + + value = val; +} + +asDWORD asCAtomic::atomicInc() +{ + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + + return asAtomicInc((int&)value); +} + +asDWORD asCAtomic::atomicDec() +{ + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + + return asAtomicDec((int&)value); +} + +// +// The following code implements the atomicInc and atomicDec on different platforms +// +#if defined(AS_NO_THREADS) || defined(AS_NO_ATOMIC) + +int asAtomicInc(int &value) +{ + return ++value; +} + +int asAtomicDec(int &value) +{ + return --value; +} + +#elif defined(AS_XENON) /// XBox360 + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +int asAtomicInc(int &value) +{ + return InterlockedIncrement((LONG*)&value); +} + +int asAtomicDec(int &value) +{ + return InterlockedDecrement((LONG*)&value); +} + +#elif defined(AS_WIN) + +END_AS_NAMESPACE +#define WIN32_MEAN_AND_LEAN +#include +BEGIN_AS_NAMESPACE + +int asAtomicInc(int &value) +{ + return InterlockedIncrement((LONG*)&value); +} + +int asAtomicDec(int &value) +{ + asASSERT(value > 0); + return InterlockedDecrement((LONG*)&value); +} + +#elif defined(AS_LINUX) || defined(AS_BSD) || defined(AS_ILLUMOS) || defined(AS_ANDROID) + +// +// atomic_inc_and_test() and atomic_dec_and_test() from asm/atomic.h is not meant +// to be used outside the Linux kernel. Instead we should use the GNUC provided +// __sync_add_and_fetch() and __sync_sub_and_fetch() functions. +// +// Reference: http://golubenco.org/blog/atomic-operations/ +// +// These are only available in GCC 4.1 and above, so for older versions we +// use the critical sections, though it is a lot slower. +// + +int asAtomicInc(int &value) +{ + return __sync_add_and_fetch(&value, 1); +} + +int asAtomicDec(int &value) +{ + return __sync_sub_and_fetch(&value, 1); +} + +#elif defined(AS_MAC) || defined(AS_IPHONE) + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +int asAtomicInc(int &value) +{ + return OSAtomicIncrement32((int32_t*)&value); +} + +int asAtomicDec(int &value) +{ + return OSAtomicDecrement32((int32_t*)&value); +} + +#else + +// If we get here, then the configuration in as_config.h +// is wrong for the compiler/platform combination. +int ERROR_PleaseFixTheConfig[-1]; + +#endif + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_builder.cpp b/3rdparty/angelscript/src/as_builder.cpp new file mode 100644 index 0000000..075e6de --- /dev/null +++ b/3rdparty/angelscript/src/as_builder.cpp @@ -0,0 +1,5874 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_builder.cpp +// +// This is the class that manages the compilation of the scripts +// + + +#include "as_config.h" +#include "as_builder.h" +#include "as_parser.h" +#include "as_compiler.h" +#include "as_tokendef.h" +#include "as_string_util.h" +#include "as_outputbuffer.h" +#include "as_texts.h" +#include "as_scriptobject.h" +#include "as_debug.h" + +BEGIN_AS_NAMESPACE + +#ifndef AS_NO_COMPILER + +// asCSymbolTable template specializations for sGlobalVariableDescription entries +template<> +void asCSymbolTable::GetKey(const sGlobalVariableDescription *entry, asSNameSpaceNamePair &key) const +{ + asSNameSpace *ns = entry->ns; + asCString name = entry->name; + key = asSNameSpaceNamePair(ns, name); +} + +// Comparator for exact variable search +class asCCompGlobVarType : public asIFilter +{ +public: + const asCDataType &m_type; + asCCompGlobVarType(const asCDataType &type) : m_type(type) {} + + bool operator()(const void *p) const + { + const sGlobalVariableDescription* desc = reinterpret_cast(p); + return desc->datatype == m_type; + } + +private: + // The assignment operator is required for MSVC9, otherwise it will complain that it is not possible to auto generate the operator + asCCompGlobVarType &operator=(const asCCompGlobVarType &) {return *this;} +}; + +#endif + +asCBuilder::asCBuilder(asCScriptEngine *_engine, asCModule *_module) +{ + this->engine = _engine; + this->module = _module; + silent = false; +} + +asCBuilder::~asCBuilder() +{ +#ifndef AS_NO_COMPILER + asUINT n; + + // Free all functions + for( n = 0; n < functions.GetLength(); n++ ) + { + if( functions[n] ) + { + if( functions[n]->node ) + functions[n]->node->Destroy(engine); + + asDELETE(functions[n],sFunctionDescription); + } + + functions[n] = 0; + } + + // Free all global variables + asCSymbolTable::iterator it = globVariables.List(); + while( it ) + { + if( (*it)->declaredAtNode ) + (*it)->declaredAtNode->Destroy(engine); + if( (*it)->initializationNode ) + (*it)->initializationNode->Destroy(engine); + asDELETE((*it),sGlobalVariableDescription); + it++; + } + globVariables.Clear(); + + // Free all the loaded files + for( n = 0; n < scripts.GetLength(); n++ ) + { + if( scripts[n] ) + asDELETE(scripts[n],asCScriptCode); + + scripts[n] = 0; + } + + // Free all class declarations + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + if( classDeclarations[n] ) + { + if( classDeclarations[n]->node ) + classDeclarations[n]->node->Destroy(engine); + + asDELETE(classDeclarations[n],sClassDeclaration); + classDeclarations[n] = 0; + } + } + + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + if( interfaceDeclarations[n] ) + { + if( interfaceDeclarations[n]->node ) + interfaceDeclarations[n]->node->Destroy(engine); + + asDELETE(interfaceDeclarations[n],sClassDeclaration); + interfaceDeclarations[n] = 0; + } + } + + for( n = 0; n < namedTypeDeclarations.GetLength(); n++ ) + { + if( namedTypeDeclarations[n] ) + { + if( namedTypeDeclarations[n]->node ) + namedTypeDeclarations[n]->node->Destroy(engine); + + asDELETE(namedTypeDeclarations[n],sClassDeclaration); + namedTypeDeclarations[n] = 0; + } + } + + for( n = 0; n < funcDefs.GetLength(); n++ ) + { + if( funcDefs[n] ) + { + if( funcDefs[n]->node ) + funcDefs[n]->node->Destroy(engine); + + asDELETE(funcDefs[n],sFuncDef); + funcDefs[n] = 0; + } + } + + for( n = 0; n < mixinClasses.GetLength(); n++ ) + { + if( mixinClasses[n] ) + { + if( mixinClasses[n]->node ) + mixinClasses[n]->node->Destroy(engine); + + asDELETE(mixinClasses[n],sMixinClass); + mixinClasses[n] = 0; + } + } + +#endif // AS_NO_COMPILER +} + +void asCBuilder::Reset() +{ + numErrors = 0; + numWarnings = 0; + engine->preMessage.isSet = false; + +#ifndef AS_NO_COMPILER + // Clear the cache of known types + hasCachedKnownTypes = false; + knownTypes.EraseAll(); +#endif +} + +#ifndef AS_NO_COMPILER +int asCBuilder::AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy) +{ + asCScriptCode *script = asNEW(asCScriptCode); + if( script == 0 ) + return asOUT_OF_MEMORY; + + int r = script->SetCode(name, code, codeLength, makeCopy); + if( r < 0 ) + { + asDELETE(script, asCScriptCode); + return r; + } + + script->lineOffset = lineOffset; + script->idx = sectionIdx; + scripts.PushLast(script); + + return 0; +} + +void asCBuilder::EvaluateTemplateInstances(asUINT startIdx, bool keepSilent) +{ + // Backup the original message stream + bool msgCallback = engine->msgCallback; + asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc; + void *msgCallbackObj = engine->msgCallbackObj; + + // Set the new temporary message stream + asCOutputBuffer outBuffer; + if( keepSilent ) + engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL); + + // Evaluate each of the template instances that have been created since the start of the build + // TODO: This is not exactly correct, since another thread may have created template instances in parallel + for( asUINT n = startIdx; n < engine->templateInstanceTypes.GetLength(); n++ ) + { + bool dontGarbageCollect = false; + asCObjectType *tmpl = engine->templateInstanceTypes[n]; + asCScriptFunction *callback = engine->scriptFunctions[tmpl->beh.templateCallback]; + if( callback && !engine->CallGlobalFunctionRetBool(tmpl, &dontGarbageCollect, callback->sysFuncIntf, callback) ) + { + asCString sub = tmpl->templateSubTypes[0].Format(engine->nameSpaces[0]); + for( asUINT m = 1; m < tmpl->templateSubTypes.GetLength(); m++ ) + { + sub += ","; + sub += tmpl->templateSubTypes[m].Format(engine->nameSpaces[0]); + } + asCString str; + str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, tmpl->name.AddressOf(), sub.AddressOf()); + WriteError(tmpl->scriptSectionIdx >= 0 ? engine->scriptSectionNames[tmpl->scriptSectionIdx]->AddressOf() : "", str, tmpl->declaredAt&0xFFFFF, (tmpl->declaredAt>>20)&0xFFF); + } + else + { + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + tmpl->flags &= ~asOBJ_GC; + } + } + + // Restore message callback + if( keepSilent ) + { + engine->msgCallback = msgCallback; + engine->msgCallbackFunc = msgCallbackFunc; + engine->msgCallbackObj = msgCallbackObj; + } +} + +int asCBuilder::Build() +{ + Reset(); + + // The template callbacks must only be called after the subtypes have a known structure, + // otherwise the callback may think it is not possible to create the template instance, + // even though it is. + // TODO: This flag shouldn't be set globally in the engine, as it would mean that another + // thread requesting a template instance in parallel to the compilation wouldn't + // evaluate the template instance. + engine->deferValidationOfTemplateTypes = true; + asUINT numTempl = (asUINT)engine->templateInstanceTypes.GetLength(); + + ParseScripts(); + + // Compile the types first + CompileInterfaces(); + CompileClasses(numTempl); + + // Evaluate the template instances one last time, this time with error messages, as we know + // all classes have been fully built and it is known which ones will need garbage collection. + EvaluateTemplateInstances(numTempl, false); + engine->deferValidationOfTemplateTypes = false; + + // Then the global variables. Here the variables declared with auto + // will be resolved, so they can be accessed properly in the functions + CompileGlobalVariables(); + + // Finally the global functions and class methods + CompileFunctions(); + + // TODO: Attempt to reorder the initialization of global variables so that + // they do not access other uninitialized global variables out-of-order + // The builder needs to check for each of the global variable, what functions + // that are accessed, and what global variables are access by these functions. + + if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) + WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + + if( numErrors > 0 ) + return asERROR; + + // Make sure something was compiled, otherwise return an error + if( module->IsEmpty() ) + { + WriteError(TXT_NOTHING_WAS_BUILT, 0, 0); + return asERROR; + } + + return asSUCCESS; +} + +int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) +{ + Reset(); + + // Add the string to the script code + asCScriptCode *script = asNEW(asCScriptCode); + if( script == 0 ) + return asOUT_OF_MEMORY; + + script->SetCode(sectionName, code, true); + script->lineOffset = lineOffset; + script->idx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : ""); + scripts.PushLast(script); + + // Parse the string + asCParser parser(this); + if( parser.ParseScript(scripts[0]) < 0 ) + return asERROR; + + asCScriptNode *node = parser.GetScriptNode(); + + // Make sure there is nothing else than the global variable in the script code + if( node == 0 || + node->firstChild == 0 || + node->firstChild != node->lastChild || + node->firstChild->nodeType != snDeclaration ) + { + WriteError(TXT_ONLY_ONE_VARIABLE_ALLOWED, script, 0); + return asERROR; + } + + node = node->firstChild; + node->DisconnectParent(); + RegisterGlobalVar(node, script, module->defaultNamespace); + + CompileGlobalVariables(); + + // It is possible that the global variable initialization included anonymous functions that must be compiled too + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0); + if( r < 0 ) + break; + } + + if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) + WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(func) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); + module->scriptFunctions.RemoveValue(func); + func->ReleaseInternal(); + } + } + } + + if( numErrors > 0 ) + { + // Remove the variable from the module, if it was registered + if( globVariables.GetSize() > 0 ) + module->RemoveGlobalVar(module->GetGlobalVarCount()-1); + + return asERROR; + } + + return 0; +} +#endif + +int asCBuilder::ValidateDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func) +{ + int firstArgWithDefaultValue = -1; + for( asUINT n = 0; n < func->defaultArgs.GetLength(); n++ ) + { + if( func->defaultArgs[n] ) + firstArgWithDefaultValue = n; + else if( firstArgWithDefaultValue >= 0 ) + { + asCString str; + str.Format(TXT_DEF_ARG_MISSING_IN_FUNC_s, func->GetDeclaration()); + WriteError(str, script, node); + return asINVALID_DECLARATION; + } + } + + return 0; +} + +#ifndef AS_NO_COMPILER +// This function will verify if the newly created function will conflict another overload due to having +// identical function arguments that are not default args, e.g: foo(int) and foo(int, int=0) +int asCBuilder::CheckForConflictsDueToDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func, asCObjectType *objType) +{ + // TODO: Implement for global functions too + if( func->objectType == 0 || objType == 0 ) return 0; + + asCArray funcs; + GetObjectMethodDescriptions(func->name.AddressOf(), objType, funcs, false); + for( asUINT n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *func2 = engine->scriptFunctions[funcs[n]]; + if( func == func2 ) + continue; + + if( func->IsReadOnly() != func2->IsReadOnly() ) + continue; + + bool match = true; + asUINT p = 0; + for( ; p < func->parameterTypes.GetLength() && p < func2->parameterTypes.GetLength(); p++ ) + { + // Only verify until the first argument with default args + if( (func->defaultArgs.GetLength() > p && func->defaultArgs[p]) || + (func2->defaultArgs.GetLength() > p && func2->defaultArgs[p]) ) + break; + + if( func->parameterTypes[p] != func2->parameterTypes[p] || + func->inOutFlags[p] != func2->inOutFlags[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( !((p >= func->parameterTypes.GetLength() && p < func2->defaultArgs.GetLength() && func2->defaultArgs[p]) || + (p >= func2->parameterTypes.GetLength() && p < func->defaultArgs.GetLength() && func->defaultArgs[p])) ) + { + // The argument lists match for the full length of the shorter, but the next + // argument on the longer does not have a default arg so there is no conflict + match = false; + } + } + + if( match ) + { + WriteWarning(TXT_OVERLOAD_CONFLICTS_DUE_TO_DEFAULT_ARGS, script, node); + WriteInfo(func->GetDeclaration(), script, node); + WriteInfo(func2->GetDeclaration(), script, node); + break; + } + } + + return 0; +} + +int asCBuilder::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc) +{ + asASSERT(outFunc != 0); + + Reset(); + + // Add the string to the script code + asCScriptCode *script = asNEW(asCScriptCode); + if( script == 0 ) + return asOUT_OF_MEMORY; + + script->SetCode(sectionName, code, true); + script->lineOffset = lineOffset; + script->idx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : ""); + scripts.PushLast(script); + + // Parse the string + asCParser parser(this); + if( parser.ParseScript(scripts[0]) < 0 ) + return asERROR; + + asCScriptNode *node = parser.GetScriptNode(); + + // Make sure there is nothing else than the function in the script code + if( node == 0 || + node->firstChild == 0 || + node->firstChild != node->lastChild || + node->firstChild->nodeType != snFunction ) + { + WriteError(TXT_ONLY_ONE_FUNCTION_ALLOWED, script, 0); + return asERROR; + } + + // Find the function node + node = node->firstChild; + + // Create the function + bool isConstructor, isDestructor, isPrivate, isProtected, isFinal, isOverride, isShared; + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, compileFlags & asCOMP_ADD_TO_MODULE ? module : 0, asFUNC_SCRIPT); + if( func == 0 ) + return asOUT_OF_MEMORY; + + GetParsedFunctionDetails(node, scripts[0], 0, func->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, func->defaultArgs, func->isReadOnly, isConstructor, isDestructor, isPrivate, isProtected, isFinal, isOverride, isShared, module->defaultNamespace); + func->id = engine->GetNextScriptFunctionId(); + func->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : ""); + int row, col; + scripts[0]->ConvertPosToRowCol(node->tokenPos, &row, &col); + func->scriptData->declaredAt = (row & 0xFFFFF)|((col & 0xFFF)<<20); + func->nameSpace = module->defaultNamespace; + + // Make sure the default args are declared correctly + int r = ValidateDefaultArgs(script, node, func); + if( r < 0 ) + { + func->ReleaseInternal(); + return asERROR; + } + + // Tell the engine that the function exists already so the compiler can access it + if( compileFlags & asCOMP_ADD_TO_MODULE ) + { + r = CheckNameConflict(func->name.AddressOf(), node, scripts[0], module->defaultNamespace); + if( r < 0 ) + { + func->ReleaseInternal(); + return asERROR; + } + + module->globalFunctions.Put(func); + + module->AddScriptFunction(func); + } + else + engine->AddScriptFunction(func); + + // Fill in the function info for the builder too + node->DisconnectParent(); + sFunctionDescription *funcDesc = asNEW(sFunctionDescription); + if( funcDesc == 0 ) + { + func->ReleaseInternal(); + return asOUT_OF_MEMORY; + } + + functions.PushLast(funcDesc); + funcDesc->script = scripts[0]; + funcDesc->node = node; + funcDesc->name = func->name; + funcDesc->funcId = func->id; + funcDesc->paramNames = func->parameterNames; + funcDesc->isExistingShared = false; + + // This must be done in a loop, as it is possible that additional functions get declared as lambda's in the code + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *f = engine->scriptFunctions[functions[n]->funcId]; + r = compiler.CompileFunction(this, functions[n]->script, f->parameterNames, functions[n]->node, f, 0); + if( r < 0 ) + break; + } + + if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) + WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( !(compileFlags & asCOMP_ADD_TO_MODULE) || numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(f) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(f)); + module->scriptFunctions.RemoveValue(f); + f->ReleaseInternal(); + } + } + } + + if( numErrors > 0 ) + { + // Release the function pointer that would otherwise be returned if no errors occured + func->ReleaseInternal(); + + return asERROR; + } + + // Return the function + *outFunc = func; + + return asSUCCESS; +} + +void asCBuilder::ParseScripts() +{ + TimeIt("asCBuilder::ParseScripts"); + + asCArray parsers((int)scripts.GetLength()); + + // Parse all the files as if they were one + asUINT n = 0; + for( n = 0; n < scripts.GetLength(); n++ ) + { + asCParser *parser = asNEW(asCParser)(this); + if( parser != 0 ) + { + parsers.PushLast(parser); + + // Parse the script file + parser->ParseScript(scripts[n]); + } + } + + if (numErrors == 0) + { + // Find all type declarations + for (n = 0; n < scripts.GetLength(); n++) + { + asCScriptNode *node = parsers[n]->GetScriptNode(); + RegisterTypesFromScript(node, scripts[n], engine->nameSpaces[0]); + } + + // Before moving forward the builder must establish the relationship between types + // so that a derived type can see the child types of the parent type. + DetermineTypeRelations(); + + // Complete function definitions (defining returntype and parameters) + for( n = 0; n < funcDefs.GetLength(); n++ ) + CompleteFuncDef(funcDefs[n]); + + // Register script methods found in the interfaces + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = interfaceDeclarations[n]; + asCScriptNode *node = decl->node->firstChild->next; + + // Skip list of inherited interfaces + while( node && node->nodeType == snIdentifier ) + node = node->next; + + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snFunction ) + { + node->DisconnectParent(); + RegisterScriptFunctionFromNode(node, decl->script, CastToObjectType(decl->typeInfo), true, false, 0, decl->isExistingShared); + } + else if( node->nodeType == snVirtualProperty ) + { + node->DisconnectParent(); + RegisterVirtualProperty(node, decl->script, CastToObjectType(decl->typeInfo), true, false, 0, decl->isExistingShared); + } + + node = next; + } + } + + // Register script methods found in the classes + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + + asCScriptNode *node = decl->node->firstChild->next; + + // Skip list of classes and interfaces + while( node && node->nodeType == snIdentifier ) + node = node->next; + + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snFunction ) + { + node->DisconnectParent(); + RegisterScriptFunctionFromNode(node, decl->script, CastToObjectType(decl->typeInfo), false, false, 0, decl->isExistingShared); + } + else if( node->nodeType == snVirtualProperty ) + { + node->DisconnectParent(); + RegisterVirtualProperty(node, decl->script, CastToObjectType(decl->typeInfo), false, false, 0, decl->isExistingShared); + } + + node = next; + } + + // Make sure the default factory & constructor exists for classes + asCObjectType *ot = CastToObjectType(decl->typeInfo); + if( ot->beh.construct == engine->scriptTypeBehaviours.beh.construct ) + { + if( ot->beh.constructors.GetLength() == 1 || engine->ep.alwaysImplDefaultConstruct ) + { + AddDefaultConstructor(ot, decl->script); + } + else + { + // As the class has another constructor we shouldn't provide the default constructor + if( ot->beh.construct ) + { + engine->scriptFunctions[ot->beh.construct]->ReleaseInternal(); + ot->beh.construct = 0; + ot->beh.constructors.RemoveIndex(0); + } + if( ot->beh.factory ) + { + engine->scriptFunctions[ot->beh.factory]->ReleaseInternal(); + ot->beh.factory = 0; + ot->beh.factories.RemoveIndex(0); + } + // Only remove the opAssign method if the script hasn't provided one + if( ot->beh.copy == engine->scriptTypeBehaviours.beh.copy ) + { + engine->scriptFunctions[ot->beh.copy]->ReleaseInternal(); + ot->beh.copy = 0; + } + } + } + } + + // Find other global nodes + for( n = 0; n < scripts.GetLength(); n++ ) + { + // Find other global nodes + asCScriptNode *node = parsers[n]->GetScriptNode(); + RegisterNonTypesFromScript(node, scripts[n], engine->nameSpaces[0]); + } + } + + for( n = 0; n < parsers.GetLength(); n++ ) + { + asDELETE(parsers[n],asCParser); + } +} + +void asCBuilder::RegisterTypesFromScript(asCScriptNode *node, asCScriptCode *script, asSNameSpace *ns) +{ + asASSERT(node->nodeType == snScript); + + // Find structure definitions first + node = node->firstChild; + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snNamespace ) + { + // Recursively register the entities defined in the namespace + asCString nsName; + nsName.Assign(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength); + if( ns->name != "" ) + nsName = ns->name + "::" + nsName; + + asSNameSpace *nsChild = engine->AddNameSpace(nsName.AddressOf()); + RegisterTypesFromScript(node->lastChild, script, nsChild); + } + else + { + if( node->nodeType == snClass ) + { + node->DisconnectParent(); + RegisterClass(node, script, ns); + } + else if( node->nodeType == snInterface ) + { + node->DisconnectParent(); + RegisterInterface(node, script, ns); + } + else if( node->nodeType == snEnum ) + { + node->DisconnectParent(); + RegisterEnum(node, script, ns); + } + else if( node->nodeType == snTypedef ) + { + node->DisconnectParent(); + RegisterTypedef(node, script, ns); + } + else if( node->nodeType == snFuncDef ) + { + node->DisconnectParent(); + RegisterFuncDef(node, script, ns, 0); + } + else if( node->nodeType == snMixin ) + { + node->DisconnectParent(); + RegisterMixinClass(node, script, ns); + } + } + + node = next; + } +} + +void asCBuilder::RegisterNonTypesFromScript(asCScriptNode *node, asCScriptCode *script, asSNameSpace *ns) +{ + node = node->firstChild; + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snNamespace ) + { + // Determine the name of the namespace + asCString nsName; + nsName.Assign(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength); + if( ns->name != "" ) + nsName = ns->name + "::" + nsName; + + // Declare the namespace, then add the entities + asSNameSpace *nsChild = engine->AddNameSpace(nsName.AddressOf()); + RegisterNonTypesFromScript(node->lastChild, script, nsChild); + } + else + { + node->DisconnectParent(); + if( node->nodeType == snFunction ) + RegisterScriptFunctionFromNode(node, script, 0, false, true, ns); + else if( node->nodeType == snDeclaration ) + RegisterGlobalVar(node, script, ns); + else if( node->nodeType == snVirtualProperty ) + RegisterVirtualProperty(node, script, 0, false, true, ns); + else if( node->nodeType == snImport ) + RegisterImportedFunction(module->GetNextImportedFunctionId(), node, script, ns); + else + { + // Unused script node + int r, c; + script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteWarning(script->name, TXT_UNUSED_SCRIPT_NODE, r, c); + + node->Destroy(engine); + } + } + + node = next; + } +} + +void asCBuilder::CompileFunctions() +{ + // Compile each function + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + sFunctionDescription *current = functions[n]; + if( current == 0 ) continue; + + // Don't compile the function again if it was an existing shared function + if( current->isExistingShared ) continue; + + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[current->funcId]; + + // Find the class declaration for constructors + sClassDeclaration *classDecl = 0; + if( current->objType && current->name == current->objType->name ) + { + for( asUINT c = 0; c < classDeclarations.GetLength(); c++ ) + { + if( classDeclarations[c]->typeInfo == current->objType ) + { + classDecl = classDeclarations[c]; + break; + } + } + + asASSERT( classDecl ); + } + + if( current->node ) + { + int r, c; + current->script->ConvertPosToRowCol(current->node->tokenPos, &r, &c); + + asCString str = func->GetDeclarationStr(); + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(current->script->name, str, r, c, true); + + // When compiling a constructor need to pass the class declaration for member initializations + compiler.CompileFunction(this, current->script, current->paramNames, current->node, func, classDecl); + + engine->preMessage.isSet = false; + } + else if( current->objType && current->name == current->objType->name ) + { + asCScriptNode *node = classDecl->node; + + int r = 0, c = 0; + if( node ) + current->script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str = func->GetDeclarationStr(); + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(current->script->name, str, r, c, true); + + // This is the default constructor that is generated + // automatically if not implemented by the user. + compiler.CompileDefaultConstructor(this, current->script, node, func, classDecl); + + engine->preMessage.isSet = false; + } + else + { + asASSERT( false ); + } + } +} +#endif + +// Called from module and engine +int asCBuilder::ParseDataType(const char *datatype, asCDataType *result, asSNameSpace *implicitNamespace, bool isReturnType) +{ + Reset(); + + asCScriptCode source; + source.SetCode("", datatype, true); + + asCParser parser(this); + int r = parser.ParseDataType(&source, isReturnType); + if( r < 0 ) + return asINVALID_TYPE; + + // Get data type and property name + asCScriptNode *dataType = parser.GetScriptNode()->firstChild; + + *result = CreateDataTypeFromNode(dataType, &source, implicitNamespace, true); + if( isReturnType ) + *result = ModifyDataTypeFromNode(*result, dataType->next, &source, 0, 0); + + if( numErrors > 0 ) + return asINVALID_TYPE; + + return asSUCCESS; +} + +int asCBuilder::ParseTemplateDecl(const char *decl, asCString *name, asCArray &subtypeNames) +{ + Reset(); + + asCScriptCode source; + source.SetCode("", decl, true); + + asCParser parser(this); + int r = parser.ParseTemplateDecl(&source); + if( r < 0 ) + return asINVALID_TYPE; + + // Get the template name and subtype names + asCScriptNode *node = parser.GetScriptNode()->firstChild; + + name->Assign(&decl[node->tokenPos], node->tokenLength); + while( (node = node->next) != 0 ) + { + asCString subtypeName; + subtypeName.Assign(&decl[node->tokenPos], node->tokenLength); + subtypeNames.PushLast(subtypeName); + } + + // TODO: template: check for name conflicts + + if( numErrors > 0 ) + return asINVALID_DECLARATION; + + return asSUCCESS; +} + +int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &name, asCDataType &type, asSNameSpace *ns) +{ + // Either datatype or namespace must be informed + asASSERT( dt || ns ); + + Reset(); + + if( dt ) + { + // Verify that the object type exist + if( CastToObjectType(dt->GetTypeInfo()) == 0 ) + return asINVALID_OBJECT; + } + + // Check property declaration and type + asCScriptCode source; + source.SetCode(TXT_PROPERTY, decl, true); + + asCParser parser(this); + int r = parser.ParsePropertyDeclaration(&source); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Get data type + asCScriptNode *dataType = parser.GetScriptNode()->firstChild; + + // Check if the property is declared 'by reference' + bool isReference = (dataType->next->tokenType == ttAmp); + + // Get the name of the property + asCScriptNode *nameNode = isReference ? dataType->next->next : dataType->next; + + // If an object property is registered, then use the + // object's namespace, otherwise use the specified namespace + type = CreateDataTypeFromNode(dataType, &source, dt ? dt->GetTypeInfo()->nameSpace : ns); + name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); + type.MakeReference(isReference); + + // Validate that the type really can be a registered property + // We cannot use CanBeInstantiated, as it is allowed to register + // properties of type that cannot otherwise be instantiated + if( type.IsFuncdef() && !type.IsObjectHandle() ) + { + // Function definitions must always be handles + return asINVALID_DECLARATION; + } + + // Verify property name + if( dt ) + { + if( CheckNameConflictMember(dt->GetTypeInfo(), name.AddressOf(), nameNode, &source, true) < 0 ) + return asNAME_TAKEN; + } + else + { + if( CheckNameConflict(name.AddressOf(), nameNode, &source, ns) < 0 ) + return asNAME_TAKEN; + } + + if( numErrors > 0 ) + return asINVALID_DECLARATION; + + return asSUCCESS; +} + +#ifndef AS_NO_COMPILER +asCObjectProperty *asCBuilder::GetObjectProperty(asCDataType &obj, const char *prop) +{ + asASSERT(CastToObjectType(obj.GetTypeInfo()) != 0); + + // TODO: optimize: Improve linear search + asCArray &props = CastToObjectType(obj.GetTypeInfo())->properties; + for( asUINT n = 0; n < props.GetLength(); n++ ) + { + if( props[n]->name == prop ) + { + if( module->accessMask & props[n]->accessMask ) + return props[n]; + else + return 0; + } + } + + return 0; +} +#endif + +bool asCBuilder::DoesGlobalPropertyExist(const char *prop, asSNameSpace *ns, asCGlobalProperty **outProp, sGlobalVariableDescription **outDesc, bool *isAppProp) +{ + if( outProp ) *outProp = 0; + if( outDesc ) *outDesc = 0; + if( isAppProp ) *isAppProp = false; + + // Check application registered properties + asCString name(prop); + asCGlobalProperty *globProp = engine->registeredGlobalProps.GetFirst(ns, name); + if( globProp ) + { + if( isAppProp ) *isAppProp = true; + if( outProp ) *outProp = globProp; + return true; + } + +#ifndef AS_NO_COMPILER + // Check properties being compiled now + sGlobalVariableDescription* desc = globVariables.GetFirst(ns, prop); + if( desc && !desc->isEnumValue ) + { + if( outProp ) *outProp = desc->property; + if( outDesc ) *outDesc = desc; + return true; + } +#endif + + // Check previously compiled global variables + if( module ) + { + globProp = module->scriptGlobals.GetFirst(ns, prop); + if( globProp ) + { + if( outProp ) *outProp = globProp; + return true; + } + } + + return false; +} + +asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, asSNameSpace *ns, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue, bool *isAppProp) +{ + if( isCompiled ) *isCompiled = true; + if( isPureConstant ) *isPureConstant = false; + if( isAppProp ) *isAppProp = false; + if( constantValue ) *constantValue = 0; + + asCGlobalProperty *globProp = 0; + sGlobalVariableDescription *globDesc = 0; + if( DoesGlobalPropertyExist(prop, ns, &globProp, &globDesc, isAppProp) ) + { +#ifndef AS_NO_COMPILER + if( globDesc ) + { + // The property was declared in this build call, check if it has been compiled successfully already + if( isCompiled ) *isCompiled = globDesc->isCompiled; + if( isPureConstant ) *isPureConstant = globDesc->isPureConstant; + if( constantValue ) *constantValue = globDesc->constantValue; + } + else +#endif + if( isAppProp ) + { + // Don't return the property if the module doesn't have access to it + if( !(module->accessMask & globProp->accessMask) ) + globProp = 0; + } + return globProp; + } + + return 0; +} + +int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray *paramAutoHandles, bool *returnAutoHandle, asSNameSpace *ns, asCScriptNode **listPattern, asCObjectType **outParentClass) +{ + asASSERT( objType || ns ); + + if (listPattern) + *listPattern = 0; + if (outParentClass) + *outParentClass = 0; + + // TODO: Can't we use GetParsedFunctionDetails to do most of what is done in this function? + + Reset(); + + asCScriptCode source; + source.SetCode(TXT_SYSTEM_FUNCTION, decl, true); + + asCParser parser(this); + int r = parser.ParseFunctionDefinition(&source, listPattern != 0); + if( r < 0 ) + return asINVALID_DECLARATION; + + asCScriptNode *node = parser.GetScriptNode(); + + // Determine scope + asCScriptNode *n = node->firstChild->next->next; + asCObjectType *parentClass = 0; + func->nameSpace = GetNameSpaceFromNode(n, &source, ns, &n, &parentClass); + if( func->nameSpace == 0 && parentClass == 0 ) + return asINVALID_DECLARATION; + if (parentClass && func->funcType != asFUNC_FUNCDEF) + return asINVALID_DECLARATION; + + if (outParentClass) + *outParentClass = parentClass; + + // Find name + func->name.Assign(&source.code[n->tokenPos], n->tokenLength); + + // Initialize a script function object for registration + bool autoHandle; + + // Scoped reference types are allowed to use handle when returned from application functions + func->returnType = CreateDataTypeFromNode(node->firstChild, &source, objType ? objType->nameSpace : ns, true, parentClass ? parentClass : objType); + func->returnType = ModifyDataTypeFromNode(func->returnType, node->firstChild->next, &source, 0, &autoHandle); + if( autoHandle && (!func->returnType.IsObjectHandle() || func->returnType.IsReference()) ) + return asINVALID_DECLARATION; + if( returnAutoHandle ) *returnAutoHandle = autoHandle; + + // Reference types cannot be returned by value from system functions + if( isSystemFunction && + (func->returnType.GetTypeInfo() && + (func->returnType.GetTypeInfo()->flags & asOBJ_REF)) && + !(func->returnType.IsReference() || + func->returnType.IsObjectHandle()) ) + return asINVALID_DECLARATION; + + // Count number of parameters + int paramCount = 0; + asCScriptNode *paramList = n->next; + n = paramList->firstChild; + while( n ) + { + paramCount++; + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + n = n->next; + + if( n && n->nodeType == snExpression ) + n = n->next; + } + + // Preallocate memory + func->parameterTypes.Allocate(paramCount, false); + func->parameterNames.SetLength(paramCount); + func->inOutFlags.Allocate(paramCount, false); + func->defaultArgs.Allocate(paramCount, false); + if( paramAutoHandles ) paramAutoHandles->Allocate(paramCount, false); + + n = paramList->firstChild; + asUINT index = 0; + while( n ) + { + asETypeModifiers inOutFlags; + asCDataType type = CreateDataTypeFromNode(n, &source, objType ? objType->nameSpace : ns, false, parentClass ? parentClass : objType); + type = ModifyDataTypeFromNode(type, n->next, &source, &inOutFlags, &autoHandle); + + // Reference types cannot be passed by value to system functions + if( isSystemFunction && + (type.GetTypeInfo() && + (type.GetTypeInfo()->flags & asOBJ_REF)) && + !(type.IsReference() || + type.IsObjectHandle()) ) + return asINVALID_DECLARATION; + + // Store the parameter type + func->parameterTypes.PushLast(type); + func->inOutFlags.PushLast(inOutFlags); + + // Don't permit void parameters + if( type.GetTokenType() == ttVoid ) + return asINVALID_DECLARATION; + + if( autoHandle && (!type.IsObjectHandle() || type.IsReference()) ) + return asINVALID_DECLARATION; + + if( paramAutoHandles ) paramAutoHandles->PushLast(autoHandle); + + // Make sure that var type parameters are references + if( type.GetTokenType() == ttQuestion && + !type.IsReference() ) + return asINVALID_DECLARATION; + + // Move to next parameter + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + { + func->parameterNames[index] = asCString(&source.code[n->tokenPos], n->tokenLength); + n = n->next; + } + ++index; + + if( n && n->nodeType == snExpression ) + { + // Strip out white space and comments to better share the string + asCString *defaultArgStr = asNEW(asCString); + if( defaultArgStr ) + { + *defaultArgStr = GetCleanExpressionString(n, &source); + func->defaultArgs.PushLast(defaultArgStr); + } + + n = n->next; + } + else + func->defaultArgs.PushLast(0); + } + + // Set the read-only flag if const is declared after parameter list + n = paramList->next; + if( n && n->nodeType == snUndefined && n->tokenType == ttConst ) + { + if( objType == 0 ) + return asINVALID_DECLARATION; + func->isReadOnly = true; + + n = n->next; + } + else + func->isReadOnly = false; + + // If the caller expects a list pattern, check for the existence, else report an error if not + if( listPattern ) + { + if( n == 0 || n->nodeType != snListPattern ) + return asINVALID_DECLARATION; + else + { + *listPattern = n; + n->DisconnectParent(); + } + } + else + { + if( n ) + return asINVALID_DECLARATION; + } + + // Make sure the default args are declared correctly + ValidateDefaultArgs(&source, node, func); + + if( numErrors > 0 || numWarnings > 0 ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCBuilder::ParseVariableDeclaration(const char *decl, asSNameSpace *implicitNamespace, asCString &outName, asSNameSpace *&outNamespace, asCDataType &outDt) +{ + Reset(); + + asCScriptCode source; + source.SetCode(TXT_VARIABLE_DECL, decl, true); + + asCParser parser(this); + + int r = parser.ParsePropertyDeclaration(&source); + if( r < 0 ) + return asINVALID_DECLARATION; + + asCScriptNode *node = parser.GetScriptNode(); + + // Determine the scope from declaration + asCScriptNode *n = node->firstChild->next; + // TODO: child funcdef: The parentType will be set if the scope is actually a type rather than a namespace + outNamespace = GetNameSpaceFromNode(n, &source, implicitNamespace, &n); + if( outNamespace == 0 ) + return asINVALID_DECLARATION; + + // Find name + outName.Assign(&source.code[n->tokenPos], n->tokenLength); + + // Initialize a script variable object for registration + outDt = CreateDataTypeFromNode(node->firstChild, &source, implicitNamespace); + + if( numErrors > 0 || numWarnings > 0 ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCBuilder::CheckNameConflictMember(asCTypeInfo *t, const char *name, asCScriptNode *node, asCScriptCode *code, bool isProperty) +{ + // It's not necessary to check against object types + + asCObjectType *ot = CastToObjectType(t); + if (!ot) + return 0; + + // TODO: optimize: Improve linear search + asCArray &props = ot->properties; + for( asUINT n = 0; n < props.GetLength(); n++ ) + { + if( props[n]->name == name ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_OBJ_PROPERTY, name); + WriteError(str, code, node); + } + + return -1; + } + } + + asCArray &funcdefs = ot->childFuncDefs; + for (asUINT n = 0; n < funcdefs.GetLength(); n++) + { + if (funcdefs[n]->name == name) + { + if (code) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_IS_FUNCDEF, name); + WriteError(str, code, node); + } + + return -1; + } + } + + // Property names must be checked against method names + if( isProperty ) + { + asCArray methods = ot->methods; + for( asUINT n = 0; n < methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[methods[n]]->name == name ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_METHOD, name); + WriteError(str, code, node); + } + + return -1; + } + } + } + + return 0; +} + +int asCBuilder::CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code, asSNameSpace *ns) +{ + // Check against registered object types + // TODO: Must check against registered funcdefs too + if( engine->GetRegisteredType(name, ns) != 0 ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_EXTENDED_TYPE, name); + WriteError(str, code, node); + } + + return -1; + } + + // Check against global properties + if( DoesGlobalPropertyExist(name, ns) ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY, name); + WriteError(str, code, node); + } + + return -1; + } + + // TODO: Property names must be checked against function names + +#ifndef AS_NO_COMPILER + // Check against class types + asUINT n; + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + if( classDeclarations[n]->name == name && + classDeclarations[n]->typeInfo->nameSpace == ns ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_STRUCT, name); + WriteError(str, code, node); + } + + return -1; + } + } + + // Check against named types + for( n = 0; n < namedTypeDeclarations.GetLength(); n++ ) + { + if( namedTypeDeclarations[n]->name == name && + namedTypeDeclarations[n]->typeInfo->nameSpace == ns ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_IS_NAMED_TYPE, name); + WriteError(str, code, node); + } + + return -1; + } + } + + // Must check for name conflicts with funcdefs + for( n = 0; n < funcDefs.GetLength(); n++ ) + { + if( funcDefs[n]->name == name && + module->funcDefs[funcDefs[n]->idx]->nameSpace == ns ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_IS_FUNCDEF, name); + WriteError(str, code, node); + } + + return -1; + } + } + + // Check against mixin classes + if( GetMixinClass(name, ns) ) + { + if( code ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_IS_MIXIN, name); + WriteError(str, code, node); + } + + return -1; + } +#endif + + return 0; +} + +#ifndef AS_NO_COMPILER +sMixinClass *asCBuilder::GetMixinClass(const char *name, asSNameSpace *ns) +{ + for( asUINT n = 0; n < mixinClasses.GetLength(); n++ ) + if( mixinClasses[n]->name == name && + mixinClasses[n]->ns == ns ) + return mixinClasses[n]; + + return 0; +} + +int asCBuilder::RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns, asCObjectType *parent) +{ + // namespace and parent are exclusively mutual + asASSERT((ns == 0 && parent) || (ns && parent == 0)); + + // TODO: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case + // an error should be given if any of the arguments/return type is not + // shared. + + // Find the name + asASSERT( node->firstChild->nodeType == snDataType ); + asCScriptNode *n = node->firstChild->next->next; + + asCString name; + name.Assign(&file->code[n->tokenPos], n->tokenLength); + + // Check for name conflict with other types + if (ns) + { + int r = CheckNameConflict(name.AddressOf(), node, file, ns); + if (asSUCCESS != r) + { + node->Destroy(engine); + return r; + } + } + else + { + int r = CheckNameConflictMember(parent, name.AddressOf(), node, file, false); + if (asSUCCESS != r) + { + node->Destroy(engine); + return r; + } + } + + // The function definition should be stored as a asCScriptFunction so that the application + // can use the asIScriptFunction interface to enumerate the return type and parameters + + // The return type and parameter types aren't determined in this function. A second pass is + // necessary after all type declarations have been identified. The second pass is implemented + // in CompleteFuncDef(). + + sFuncDef *fd = asNEW(sFuncDef); + if( fd == 0 ) + { + node->Destroy(engine); + return asOUT_OF_MEMORY; + } + + fd->name = name; + fd->node = node; + fd->script = file; + fd->idx = module->AddFuncDef(name, ns, parent); + + funcDefs.PushLast(fd); + + return 0; +} + +void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) +{ + asCArray defaultArgs; + bool isConstMethod; + bool isConstructor; + bool isDestructor; + bool isProtected; + bool isPrivate; + bool isOverride; + bool isFinal; + bool isShared; + + asCFuncdefType *fdt = module->funcDefs[funcDef->idx]; + asASSERT( fdt ); + asCScriptFunction *func = fdt->funcdef; + + // TODO: It should be possible to declare funcdef as shared. In this case a compiler error will be given if any of the types it uses are not shared + asSNameSpace *implicitNs = func->nameSpace ? func->nameSpace : fdt->parentClass->nameSpace; + GetParsedFunctionDetails(funcDef->node, funcDef->script, fdt->parentClass, funcDef->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, implicitNs); + + // There should not be any defaultArgs, but if there are any we need to delete them to avoid leaks + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + // All funcdefs are shared, unless one of the parameter types or return type is not shared + isShared = true; + if( func->returnType.GetTypeInfo() && !func->returnType.GetTypeInfo()->IsShared() ) + isShared = false; + for( asUINT n = 0; isShared && n < func->parameterTypes.GetLength(); n++ ) + if( func->parameterTypes[n].GetTypeInfo() && !func->parameterTypes[n].GetTypeInfo()->IsShared() ) + isShared = false; + func->isShared = isShared; + + // Check if there is another identical funcdef from another module and if so reuse that instead + if( func->isShared ) + { + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + { + asCFuncdefType *fdt2 = engine->funcDefs[n]; + if( fdt2 == 0 || fdt == fdt2 ) + continue; + + if( !fdt2->funcdef->isShared ) + continue; + + if( fdt2->name == fdt->name && + fdt2->nameSpace == fdt->nameSpace && + fdt2->funcdef->IsSignatureExceptNameEqual(func) ) + { + // Replace our funcdef for the existing one + funcDef->idx = fdt2->funcdef->id; + module->funcDefs[module->funcDefs.IndexOf(fdt)] = fdt2; + fdt2->AddRefInternal(); + + engine->funcDefs.RemoveValue(fdt); + + fdt->ReleaseInternal(); + break; + } + } + } +} + +int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + // Has the application disabled global vars? + if( engine->ep.disallowGlobalVars ) + WriteError(TXT_GLOBAL_VARS_NOT_ALLOWED, file, node); + + // What data type is it? + asCDataType type = CreateDataTypeFromNode(node->firstChild, file, ns); + + if( !type.CanBeInstantiated() ) + { + asCString str; + if( type.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, type.Format(ns).AddressOf()); + else if( type.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, type.Format(ns).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format(ns).AddressOf()); + + WriteError(str, file, node); + } + + asCScriptNode *n = node->firstChild->next; + + while( n ) + { + // Verify that the name isn't taken + asCString name(&file->code[n->tokenPos], n->tokenLength); + CheckNameConflict(name.AddressOf(), n, file, ns); + + // Register the global variable + sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription); + if( gvar == 0 ) + { + node->Destroy(engine); + return asOUT_OF_MEMORY; + } + + gvar->script = file; + gvar->name = name; + gvar->isCompiled = false; + gvar->datatype = type; + gvar->isEnumValue = false; + gvar->ns = ns; + + // TODO: Give error message if wrong + asASSERT(!gvar->datatype.IsReference()); + + // Allocation is done when the variable is compiled, to allow for autos + gvar->property = 0; + gvar->index = 0; + + globVariables.Put(gvar); + + + gvar->declaredAtNode = n; + n = n->next; + gvar->declaredAtNode->DisconnectParent(); + gvar->initializationNode = 0; + if( n && + ( n->nodeType == snAssignment || + n->nodeType == snArgList || + n->nodeType == snInitList ) ) + { + gvar->initializationNode = n; + n = n->next; + gvar->initializationNode->DisconnectParent(); + } + } + + node->Destroy(engine); + + return 0; +} + +int asCBuilder::RegisterMixinClass(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + asCScriptNode *cl = node->firstChild; + asASSERT( cl->nodeType == snClass ); + + asCScriptNode *n = cl->firstChild; + + // Skip potential 'final' and 'shared' tokens + while( n->tokenType == ttIdentifier && + (file->TokenEquals(n->tokenPos, n->tokenLength, FINAL_TOKEN) || + file->TokenEquals(n->tokenPos, n->tokenLength, SHARED_TOKEN)) ) + { + // Report error, because mixin class cannot be final or shared + asCString msg; + msg.Format(TXT_MIXIN_CANNOT_BE_DECLARED_AS_s, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteError(msg, file, n); + + asCScriptNode *tmp = n; + n = n->next; + + // Remove the invalid node, so compilation can continue as if it wasn't there + tmp->DisconnectParent(); + tmp->Destroy(engine); + } + + asCString name(&file->code[n->tokenPos], n->tokenLength); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + CheckNameConflict(name.AddressOf(), n, file, ns); + + sMixinClass *decl = asNEW(sMixinClass); + if( decl == 0 ) + { + node->Destroy(engine); + return asOUT_OF_MEMORY; + } + + mixinClasses.PushLast(decl); + decl->name = name; + decl->ns = ns; + decl->node = cl; + decl->script = file; + + // Clean up memory + cl->DisconnectParent(); + node->Destroy(engine); + + // Check that the mixin class doesn't contain any child types + // TODO: Add support for child types in mixin classes + n = cl->firstChild; + while (n) + { + if (n->nodeType == snFuncDef) + { + WriteError(TXT_MIXIN_CANNOT_HAVE_CHILD_TYPES, file, n); + break; + } + n = n->next; + } + + return 0; +} + +int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + asCScriptNode *n = node->firstChild; + bool isFinal = false; + bool isShared = false; + bool isAbstract = false; + + // Check the class modifiers + while( n->tokenType == ttIdentifier ) + { + if( file->TokenEquals(n->tokenPos, n->tokenLength, FINAL_TOKEN) ) + { + if( isAbstract ) + WriteError(TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT, file, n); + else + { + if( isFinal ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isFinal = true; + } + } + else if( file->TokenEquals(n->tokenPos, n->tokenLength, SHARED_TOKEN) ) + { + if( isShared ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isShared = true; + } + else if( file->TokenEquals(n->tokenPos, n->tokenLength, ABSTRACT_TOKEN) ) + { + if( isFinal ) + WriteError(TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT, file, n); + else + { + if( isAbstract ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isAbstract = true; + } + } + else + { + // This is the name of the class + break; + } + + n = n->next; + } + + asCString name(&file->code[n->tokenPos], n->tokenLength); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + CheckNameConflict(name.AddressOf(), n, file, ns); + + sClassDeclaration *decl = asNEW(sClassDeclaration); + if( decl == 0 ) + { + node->Destroy(engine); + return asOUT_OF_MEMORY; + } + + classDeclarations.PushLast(decl); + decl->name = name; + decl->script = file; + decl->node = node; + asCObjectType *st = 0; + + // If this type is shared and there already exist another shared + // type of the same name, then that one should be used instead of + // creating a new one. + if( isShared ) + { + for( asUINT i = 0; i < engine->sharedScriptTypes.GetLength(); i++ ) + { + st = CastToObjectType(engine->sharedScriptTypes[i]); + if( st && + st->IsShared() && + st->name == name && + st->nameSpace == ns && + !st->IsInterface() ) + { + // We'll use the existing type + decl->isExistingShared = true; + decl->typeInfo = st; + module->classTypes.PushLast(st); + st->AddRefInternal(); + break; + } + } + } + + if (!decl->isExistingShared) + { + // Create a new object type for this class + st = asNEW(asCObjectType)(engine); + if (st == 0) + return asOUT_OF_MEMORY; + + // By default all script classes are marked as garbage collected. + // Only after the complete structure and relationship between classes + // is known, can the flag be cleared for those objects that truly cannot + // form circular references. This is important because a template + // callback may be called with a script class before the compilation + // completes, and until it is known, the callback must assume the class + // is garbage collected. + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT | asOBJ_GC; + + if (isShared) + st->flags |= asOBJ_SHARED; + + if (isFinal) + st->flags |= asOBJ_NOINHERIT; + + if (isAbstract) + st->flags |= asOBJ_ABSTRACT; + + if (node->tokenType == ttHandle) + st->flags |= asOBJ_IMPLICIT_HANDLE; + + st->size = sizeof(asCScriptObject); + st->name = name; + st->nameSpace = ns; + st->module = module; + module->classTypes.PushLast(st); + if (isShared) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } + decl->typeInfo = st; + + // Use the default script class behaviours + st->beh = engine->scriptTypeBehaviours.beh; + + // TODO: Move this to asCObjectType so that the asCRestore can reuse it + engine->scriptFunctions[st->beh.addref]->AddRefInternal(); + engine->scriptFunctions[st->beh.release]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcEnumReferences]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcGetFlag]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcGetRefCount]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcReleaseAllReferences]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcSetFlag]->AddRefInternal(); + engine->scriptFunctions[st->beh.copy]->AddRefInternal(); + engine->scriptFunctions[st->beh.factory]->AddRefInternal(); + engine->scriptFunctions[st->beh.construct]->AddRefInternal(); + // TODO: weak: Should not do this if the class has been declared with noweak + engine->scriptFunctions[st->beh.getWeakRefFlag]->AddRefInternal(); + + // Skip to the content of the class + while (n && n->nodeType == snIdentifier) + n = n->next; + } + + // Register possible child types + while (n) + { + node = n->next; + if (n->nodeType == snFuncDef) + { + n->DisconnectParent(); + if (!decl->isExistingShared) + RegisterFuncDef(n, file, 0, st); + else + { + // Destroy the node, since it won't be used + // TODO: Should verify that the funcdef is identical to the one in the existing shared class + n->Destroy(engine); + } + } + n = node; + } + + return 0; +} + +int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + asCScriptNode *n = node->firstChild; + asCString name(&file->code[n->tokenPos], n->tokenLength); + + bool isShared = false; + if( name == SHARED_TOKEN ) + { + isShared = true; + + n = n->next; + name.Assign(&file->code[n->tokenPos], n->tokenLength); + } + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + CheckNameConflict(name.AddressOf(), n, file, ns); + + sClassDeclaration *decl = asNEW(sClassDeclaration); + if( decl == 0 ) + { + node->Destroy(engine); + return asOUT_OF_MEMORY; + } + + interfaceDeclarations.PushLast(decl); + decl->name = name; + decl->script = file; + decl->node = node; + + // If this type is shared and there already exist another shared + // type of the same name, then that one should be used instead of + // creating a new one. + if( isShared ) + { + for( asUINT i = 0; i < engine->sharedScriptTypes.GetLength(); i++ ) + { + asCObjectType *st = CastToObjectType(engine->sharedScriptTypes[i]); + if( st && + st->IsShared() && + st->name == name && + st->nameSpace == ns && + st->IsInterface() ) + { + // We'll use the existing type + decl->isExistingShared = true; + decl->typeInfo = st; + module->classTypes.PushLast(st); + st->AddRefInternal(); + return 0; + } + } + } + + // Register the object type for the interface + asCObjectType *st = asNEW(asCObjectType)(engine); + if( st == 0 ) + return asOUT_OF_MEMORY; + + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT; + + if( isShared ) + st->flags |= asOBJ_SHARED; + + st->size = 0; // Cannot be instantiated + st->name = name; + st->nameSpace = ns; + st->module = module; + module->classTypes.PushLast(st); + if( isShared ) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } + decl->typeInfo = st; + + // Use the default script class behaviours + st->beh.construct = 0; + st->beh.addref = engine->scriptTypeBehaviours.beh.addref; + engine->scriptFunctions[st->beh.addref]->AddRefInternal(); + st->beh.release = engine->scriptTypeBehaviours.beh.release; + engine->scriptFunctions[st->beh.release]->AddRefInternal(); + st->beh.copy = 0; + + return 0; +} + +void asCBuilder::CompileGlobalVariables() +{ + bool compileSucceeded = true; + + // Store state of compilation (errors, warning, output) + int currNumErrors = numErrors; + int currNumWarnings = numWarnings; + + // Backup the original message stream + bool msgCallback = engine->msgCallback; + asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc; + void *msgCallbackObj = engine->msgCallbackObj; + + // Set the new temporary message stream + asCOutputBuffer outBuffer; + engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL); + + asCOutputBuffer finalOutput; + asCScriptFunction *initFunc = 0; + + asCSymbolTable initOrder; + + // We first try to compile all the primitive global variables, and only after that + // compile the non-primitive global variables. This permits the constructors + // for the complex types to use the already initialized variables of primitive + // type. Note, we currently don't know which global variables are used in the + // constructors, so we cannot guarantee that variables of complex types are + // initialized in the correct order, so we won't reorder those. + bool compilingPrimitives = true; + + // Compile each global variable + while( compileSucceeded ) + { + compileSucceeded = false; + + int accumErrors = 0; + int accumWarnings = 0; + + // Restore state of compilation + finalOutput.Clear(); + asCSymbolTable::iterator it = globVariables.List(); + for( ; it; it++ ) + { + sGlobalVariableDescription *gvar = *it; + if( gvar->isCompiled ) + continue; + + asCByteCode init(engine); + numWarnings = 0; + numErrors = 0; + outBuffer.Clear(); + + // Skip this for now if we're not compiling complex types yet + if( compilingPrimitives && !gvar->datatype.IsPrimitive() ) + continue; + + if( gvar->declaredAtNode ) + { + int r, c; + gvar->script->ConvertPosToRowCol(gvar->declaredAtNode->tokenPos, &r, &c); + asCString str = gvar->datatype.Format(gvar->ns); + str += " " + gvar->name; + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(gvar->script->name, str, r, c, true); + } + + if( gvar->isEnumValue ) + { + int r; + if( gvar->initializationNode ) + { + asCCompiler comp(engine); + asCScriptFunction func(engine, module, asFUNC_SCRIPT); + + // Set the namespace that should be used during the compilation + func.nameSpace = gvar->datatype.GetTypeInfo()->nameSpace; + + // Temporarily switch the type of the variable to int so it can be compiled properly + asCDataType saveType; + saveType = gvar->datatype; + gvar->datatype = asCDataType::CreatePrimitive(ttInt, true); + r = comp.CompileGlobalVariable(this, gvar->script, gvar->initializationNode, gvar, &func); + gvar->datatype = saveType; + + // Make the function a dummy so it doesn't try to release objects while destroying the function + func.funcType = asFUNC_DUMMY; + } + else + { + r = 0; + + // When there is no assignment the value is the last + 1 + int enumVal = 0; + asCSymbolTable::iterator prev_it = it; + prev_it--; + if( prev_it ) + { + sGlobalVariableDescription *gvar2 = *prev_it; + if(gvar2->datatype == gvar->datatype ) + { + enumVal = int(gvar2->constantValue) + 1; + + if( !gvar2->isCompiled ) + { + int row, col; + gvar->script->ConvertPosToRowCol(gvar->declaredAtNode->tokenPos, &row, &col); + + asCString str = gvar->datatype.Format(gvar->ns); + str += " " + gvar->name; + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(gvar->script->name, str, row, col, true); + + str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, gvar2->name.AddressOf()); + WriteError(gvar->script->name, str, row, col); + r = -1; + } + } + } + + gvar->constantValue = enumVal; + } + + if( r >= 0 ) + { + // Set the value as compiled + gvar->isCompiled = true; + compileSucceeded = true; + } + } + else + { + // Compile the global variable + initFunc = asNEW(asCScriptFunction)(engine, module, asFUNC_SCRIPT); + if( initFunc == 0 ) + { + // Out of memory + return; + } + + // Set the namespace that should be used for this function + initFunc->nameSpace = gvar->ns; + + asCCompiler comp(engine); + int r = comp.CompileGlobalVariable(this, gvar->script, gvar->initializationNode, gvar, initFunc); + if( r >= 0 ) + { + // Compilation succeeded + gvar->isCompiled = true; + compileSucceeded = true; + } + else + { + // Compilation failed + initFunc->funcType = asFUNC_DUMMY; + asDELETE(initFunc, asCScriptFunction); + initFunc = 0; + } + } + + if( gvar->isCompiled ) + { + // Add warnings for this constant to the total build + if( numWarnings ) + { + currNumWarnings += numWarnings; + if( msgCallback ) + outBuffer.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj); + } + + // Determine order of variable initializations + if( gvar->property && !gvar->isEnumValue ) + initOrder.Put(gvar->property); + + // Does the function contain more than just a SUSPEND followed by a RET instruction? + if( initFunc && initFunc->scriptData->byteCode.GetLength() > 2 ) + { + // Create the init function for this variable + initFunc->id = engine->GetNextScriptFunctionId(); + engine->AddScriptFunction(initFunc); + + // Finalize the init function for this variable + initFunc->returnType = asCDataType::CreatePrimitive(ttVoid, false); + initFunc->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(gvar->script->name.AddressOf()); + if( gvar->declaredAtNode ) + { + int row, col; + gvar->script->ConvertPosToRowCol(gvar->declaredAtNode->tokenPos, &row, &col); + initFunc->scriptData->declaredAt = (row & 0xFFFFF)|((col & 0xFFF)<<20); + } + + gvar->property->SetInitFunc(initFunc); + + initFunc->ReleaseInternal(); + initFunc = 0; + } + else if( initFunc ) + { + // Destroy the function as it won't be used + initFunc->funcType = asFUNC_DUMMY; + asDELETE(initFunc, asCScriptFunction); + initFunc = 0; + } + + // Convert enums to true enum values, so subsequent compilations can access it as an enum + if( gvar->isEnumValue ) + { + asCEnumType *enumType = CastToEnumType(gvar->datatype.GetTypeInfo()); + asASSERT(NULL != enumType); + + asSEnumValue *e = asNEW(asSEnumValue); + if( e == 0 ) + { + // Out of memory + numErrors++; + return; + } + + e->name = gvar->name; + e->value = int(gvar->constantValue); + + enumType->enumValues.PushLast(e); + } + } + else + { + // Add output to final output + finalOutput.Append(outBuffer); + accumErrors += numErrors; + accumWarnings += numWarnings; + } + + engine->preMessage.isSet = false; + } + + if( !compileSucceeded ) + { + if( compilingPrimitives ) + { + // No more primitives could be compiled, so + // switch to compiling the complex variables + compilingPrimitives = false; + compileSucceeded = true; + } + else + { + // No more variables can be compiled + // Add errors and warnings to total build + currNumWarnings += accumWarnings; + currNumErrors += accumErrors; + if( msgCallback ) + finalOutput.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj); + } + } + } + + // Restore states + engine->msgCallback = msgCallback; + engine->msgCallbackFunc = msgCallbackFunc; + engine->msgCallbackObj = msgCallbackObj; + + numWarnings = currNumWarnings; + numErrors = currNumErrors; + + // Set the correct order of initialization + if( numErrors == 0 ) + { + // If the length of the arrays are not the same, then this is the compilation + // of a single variable, in which case the initialization order of the previous + // variables must be preserved. + if( module->scriptGlobals.GetSize() == initOrder.GetSize() ) + module->scriptGlobals.SwapWith(initOrder); + } + + // Delete the enum expressions + asCSymbolTableIterator it = globVariables.List(); + while( it ) + { + sGlobalVariableDescription *gvar = *it; + if( gvar->isEnumValue ) + { + // Remove from symboltable. This has to be done prior to freeing the memeory + globVariables.Erase(it.GetIndex()); + + // Destroy the gvar property + if( gvar->declaredAtNode ) + { + gvar->declaredAtNode->Destroy(engine); + gvar->declaredAtNode = 0; + } + if( gvar->initializationNode ) + { + gvar->initializationNode->Destroy(engine); + gvar->initializationNode = 0; + } + if( gvar->property ) + { + asDELETE(gvar->property, asCGlobalProperty); + gvar->property = 0; + } + + asDELETE(gvar, sGlobalVariableDescription); + } + else + it++; + } +} + +int asCBuilder::GetNamespaceAndNameFromNode(asCScriptNode *n, asCScriptCode *script, asSNameSpace *implicitNs, asSNameSpace *&outNs, asCString &outName) +{ + // TODO: child funcdef: The node might be a snScope now + asASSERT( n->nodeType == snIdentifier ); + + // Get the optional scope from the node + // TODO: child funcdef: The parentType will be set if the scope is actually a type rather than a namespace + asSNameSpace *ns = GetNameSpaceFromNode(n->firstChild, script, implicitNs, 0); + if( ns == 0 ) + return -1; + + // Get the name + asCString name(&script->code[n->lastChild->tokenPos], n->lastChild->tokenLength); + + outNs = ns; + outName = name; + + return 0; +} + +void asCBuilder::AddInterfaceFromMixinToClass(sClassDeclaration *decl, asCScriptNode *errNode, sMixinClass *mixin) +{ + // Determine what interfaces that the mixin implements + asCScriptNode *node = mixin->node; + asASSERT(node->nodeType == snClass); + + // Skip the name of the mixin + node = node->firstChild->next; + + + while( node && node->nodeType == snIdentifier ) + { + bool ok = true; + asSNameSpace *ns; + asCString name; + if( GetNamespaceAndNameFromNode(node, mixin->script, mixin->ns, ns, name) < 0 ) + ok = false; + else + { + // Find the object type for the interface + asCObjectType *objType = GetObjectType(name.AddressOf(), ns); + + // Check that the object type is an interface + if( objType && objType->IsInterface() ) + { + // Only add the interface if the class doesn't already implement it + if( !decl->typeInfo->Implements(objType) ) + AddInterfaceToClass(decl, errNode, objType); + } + else + { + WriteError(TXT_MIXIN_CLASS_CANNOT_INHERIT, mixin->script, node); + ok = false; + } + } + + if( !ok ) + { + // Remove this node so the error isn't reported again + asCScriptNode *delNode = node; + node = node->prev; + delNode->DisconnectParent(); + delNode->Destroy(engine); + } + + node = node->next; + } +} + +void asCBuilder::AddInterfaceToClass(sClassDeclaration *decl, asCScriptNode *errNode, asCObjectType *intfType) +{ + // A shared type may only implement from shared interfaces + if( decl->typeInfo->IsShared() && !intfType->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_IMPLEMENT_NON_SHARED_s, intfType->name.AddressOf()); + WriteError(msg, decl->script, errNode); + return; + } + + if( decl->isExistingShared ) + { + // If the class is an existing shared class, then just check if the + // interface exists in the original declaration too + if( !decl->typeInfo->Implements(intfType) ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, decl->typeInfo->GetName()); + WriteError(str, decl->script, errNode); + return; + } + } + else + { + // If the interface is already in the class then don't add it again + if( decl->typeInfo->Implements(intfType) ) + return; + + // Add the interface to the class + CastToObjectType(decl->typeInfo)->interfaces.PushLast(intfType); + + // Add the inherited interfaces too + // For interfaces this will be done outside to handle out-of-order declarations + if( !CastToObjectType(decl->typeInfo)->IsInterface() ) + { + for( asUINT n = 0; n < intfType->interfaces.GetLength(); n++ ) + AddInterfaceToClass(decl, errNode, intfType->interfaces[n]); + } + } +} + +void asCBuilder::CompileInterfaces() +{ + asUINT n; + + // Order the interfaces with inheritances so that the inherited + // of inherited interfaces can be added properly + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + sClassDeclaration *intfDecl = interfaceDeclarations[n]; + asCObjectType *intfType = CastToObjectType(intfDecl->typeInfo); + + if( intfType->interfaces.GetLength() == 0 ) continue; + + // If any of the derived interfaces are found after this interface, then move this to the end of the list + for( asUINT m = n+1; m < interfaceDeclarations.GetLength(); m++ ) + { + if( intfType->Implements(interfaceDeclarations[m]->typeInfo) ) + { + interfaceDeclarations.RemoveIndex(n); + interfaceDeclarations.PushLast(intfDecl); + + // Decrease index so that we don't skip an entry + n--; + break; + } + } + } + + // Now recursively add the additional inherited interfaces + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + sClassDeclaration *intfDecl = interfaceDeclarations[n]; + asCObjectType *intfType = CastToObjectType(intfDecl->typeInfo); + + // TODO: Is this really at the correct place? Hasn't the vfTableIdx already been set here? + // Co-opt the vfTableIdx value in our own methods to indicate the + // index the function should have in the table chunk for this interface. + for( asUINT d = 0; d < intfType->methods.GetLength(); d++ ) + { + asCScriptFunction *func = GetFunctionDescription(intfType->methods[d]); + func->vfTableIdx = d; + + asASSERT(func->objectType == intfType); + } + + // As new interfaces will be added to the end of the list, all + // interfaces will be traversed the same as recursively + for( asUINT m = 0; m < intfType->interfaces.GetLength(); m++ ) + { + asCObjectType *base = intfType->interfaces[m]; + + // Add any interfaces not already implemented + for( asUINT l = 0; l < base->interfaces.GetLength(); l++ ) + AddInterfaceToClass(intfDecl, intfDecl->node, base->interfaces[l]); + + // Add the methods from the implemented interface + for( asUINT l = 0; l < base->methods.GetLength(); l++ ) + { + // If the derived interface implements the same method, then don't add the base interface' method + asCScriptFunction *baseFunc = GetFunctionDescription(base->methods[l]); + asCScriptFunction *derivedFunc = 0; + bool found = false; + for( asUINT d = 0; d < intfType->methods.GetLength(); d++ ) + { + derivedFunc = GetFunctionDescription(intfType->methods[d]); + if( derivedFunc->IsSignatureEqual(baseFunc) ) + { + found = true; + break; + } + } + + if( !found ) + { + // Add the method + intfType->methods.PushLast(baseFunc->id); + baseFunc->AddRefInternal(); + } + } + } + } +} + +void asCBuilder::DetermineTypeRelations() +{ + // Determine inheritance between interfaces + for (asUINT n = 0; n < interfaceDeclarations.GetLength(); n++) + { + sClassDeclaration *intfDecl = interfaceDeclarations[n]; + asCObjectType *intfType = CastToObjectType(intfDecl->typeInfo); + + asCScriptNode *node = intfDecl->node; + asASSERT(node && node->nodeType == snInterface); + node = node->firstChild; + + // Skip the 'shared' keyword + if (intfType->IsShared()) + node = node->next; + + // Skip the name + node = node->next; + + // Verify the inherited interfaces + while (node && node->nodeType == snIdentifier) + { + asSNameSpace *ns; + asCString name; + if (GetNamespaceAndNameFromNode(node, intfDecl->script, intfType->nameSpace, ns, name) < 0) + { + node = node->next; + continue; + } + + // Find the object type for the interface + asCObjectType *objType = 0; + while (ns) + { + objType = GetObjectType(name.AddressOf(), ns); + if (objType) break; + + ns = engine->GetParentNameSpace(ns); + } + + // Check that the object type is an interface + bool ok = true; + if (objType && objType->IsInterface()) + { + // Check that the implemented interface is shared if the base interface is shared + if (intfType->IsShared() && !objType->IsShared()) + { + asCString str; + str.Format(TXT_SHARED_CANNOT_IMPLEMENT_NON_SHARED_s, objType->GetName()); + WriteError(str, intfDecl->script, node); + ok = false; + } + } + else + { + WriteError(TXT_INTERFACE_CAN_ONLY_IMPLEMENT_INTERFACE, intfDecl->script, node); + ok = false; + } + + if (ok) + { + // Make sure none of the implemented interfaces implement from this one + asCObjectType *base = objType; + while (base != 0) + { + if (base == intfType) + { + WriteError(TXT_CANNOT_IMPLEMENT_SELF, intfDecl->script, node); + ok = false; + break; + } + + // At this point there is at most one implemented interface + if (base->interfaces.GetLength()) + base = base->interfaces[0]; + else + break; + } + } + + if (ok) + AddInterfaceToClass(intfDecl, node, objType); + + // Remove the nodes so they aren't parsed again + asCScriptNode *delNode = node; + node = node->next; + delNode->DisconnectParent(); + delNode->Destroy(engine); + } + } + + // Determine class inheritances and interfaces + for (asUINT n = 0; n < classDeclarations.GetLength(); n++) + { + sClassDeclaration *decl = classDeclarations[n]; + asCScriptCode *file = decl->script; + + // Find the base class that this class inherits from + bool multipleInheritance = false; + asCScriptNode *node = decl->node->firstChild; + + while (file->TokenEquals(node->tokenPos, node->tokenLength, FINAL_TOKEN) || + file->TokenEquals(node->tokenPos, node->tokenLength, SHARED_TOKEN) || + file->TokenEquals(node->tokenPos, node->tokenLength, ABSTRACT_TOKEN)) + { + node = node->next; + } + + // Skip the name of the class + asASSERT(node->tokenType == ttIdentifier); + node = node->next; + + while (node && node->nodeType == snIdentifier) + { + asSNameSpace *ns; + asCString name; + if (GetNamespaceAndNameFromNode(node, file, decl->typeInfo->nameSpace, ns, name) < 0) + { + node = node->next; + continue; + } + + // Find the object type for the interface + asCObjectType *objType = 0; + sMixinClass *mixin = 0; + asSNameSpace *origNs = ns; + while (ns) + { + objType = GetObjectType(name.AddressOf(), ns); + if (objType == 0) + mixin = GetMixinClass(name.AddressOf(), ns); + + if (objType || mixin) + break; + + ns = engine->GetParentNameSpace(ns); + } + + if (objType == 0 && mixin == 0) + { + asCString str; + if (origNs->name == "") + str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS, name.AddressOf()); + else + str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s, name.AddressOf(), origNs->name.AddressOf()); + WriteError(str, file, node); + } + else if (mixin) + { + AddInterfaceFromMixinToClass(decl, node, mixin); + } + else if (!(objType->flags & asOBJ_SCRIPT_OBJECT) || + (objType->flags & asOBJ_NOINHERIT)) + { + // Either the class is not a script class or interface + // or the class has been declared as 'final' + asCString str; + str.Format(TXT_CANNOT_INHERIT_FROM_s_FINAL, objType->name.AddressOf()); + WriteError(str, file, node); + } + else if (objType->size != 0) + { + // The class inherits from another script class + if (!decl->isExistingShared && CastToObjectType(decl->typeInfo)->derivedFrom != 0) + { + if (!multipleInheritance) + { + WriteError(TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES, file, node); + multipleInheritance = true; + } + } + else + { + // Make sure none of the base classes inherit from this one + asCObjectType *base = objType; + bool error = false; + while (base != 0) + { + if (base == decl->typeInfo) + { + WriteError(TXT_CANNOT_INHERIT_FROM_SELF, file, node); + error = true; + break; + } + + base = base->derivedFrom; + } + + if (!error) + { + // A shared type may only inherit from other shared types + if ((decl->typeInfo->IsShared()) && !(objType->IsShared())) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_INHERIT_FROM_NON_SHARED_s, objType->name.AddressOf()); + WriteError(msg, file, node); + error = true; + } + } + + if (!error) + { + if (decl->isExistingShared) + { + // Verify that the base class is the same as the original shared type + if (CastToObjectType(decl->typeInfo)->derivedFrom != objType) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, decl->typeInfo->GetName()); + WriteError(str, file, node); + } + } + else + { + // Set the base class + CastToObjectType(decl->typeInfo)->derivedFrom = objType; + objType->AddRefInternal(); + } + } + } + } + else + { + // The class implements an interface + AddInterfaceToClass(decl, node, objType); + } + + node = node->next; + } + } +} + +// numTempl is the number of template instances that existed in the engine before the build begun +void asCBuilder::CompileClasses(asUINT numTempl) +{ + asUINT n; + asCArray toValidate((int)classDeclarations.GetLength()); + + + // Order class declarations so that base classes are compiled before derived classes. + // This will allow the derived classes to copy properties and methods in the next step. + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + asCObjectType *derived = CastToObjectType(decl->typeInfo); + asCObjectType *base = derived->derivedFrom; + + if( base == 0 ) continue; + + // If the base class is found after the derived class, then move the derived class to the end of the list + for( asUINT m = n+1; m < classDeclarations.GetLength(); m++ ) + { + sClassDeclaration *declBase = classDeclarations[m]; + if( base == declBase->typeInfo ) + { + classDeclarations.RemoveIndex(n); + classDeclarations.PushLast(decl); + + // Decrease index so that we don't skip an entry + n--; + break; + } + } + } + + // Go through each of the classes and register the object type descriptions + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + asCObjectType *ot = CastToObjectType(decl->typeInfo); + if( decl->isExistingShared ) + { + // Set the declaration as validated already, so that other + // types that contain this will accept this type + decl->validState = 1; + + // We'll still validate the declaration to make sure nothing new is + // added to the shared class that wasn't there in the previous + // compilation. We do not care if something that is there in the previous + // declaration is not included in the new declaration though. + + asASSERT( ot->interfaces.GetLength() == ot->interfaceVFTOffsets.GetLength() ); + } + + // Methods included from mixin classes should take precedence over inherited methods + IncludeMethodsFromMixins(decl); + + // Add all properties and methods from the base class + if( !decl->isExistingShared && ot->derivedFrom ) + { + asCObjectType *baseType = ot->derivedFrom; + + // The derived class inherits all interfaces from the base class + for( unsigned int m = 0; m < baseType->interfaces.GetLength(); m++ ) + { + if( !ot->Implements(baseType->interfaces[m]) ) + ot->interfaces.PushLast(baseType->interfaces[m]); + } + + // TODO: Need to check for name conflict with new class methods + + // Copy properties from base class to derived class + for( asUINT p = 0; p < baseType->properties.GetLength(); p++ ) + { + asCObjectProperty *prop = AddPropertyToClass(decl, baseType->properties[p]->name, baseType->properties[p]->type, baseType->properties[p]->isPrivate, baseType->properties[p]->isProtected, true); + + // The properties must maintain the same offset + asASSERT(prop && prop->byteOffset == baseType->properties[p]->byteOffset); UNUSED_VAR(prop); + } + + // Copy methods from base class to derived class + for( asUINT m = 0; m < baseType->methods.GetLength(); m++ ) + { + // If the derived class implements the same method, then don't add the base class' method + asCScriptFunction *baseFunc = GetFunctionDescription(baseType->methods[m]); + asCScriptFunction *derivedFunc = 0; + bool found = false; + for( asUINT d = 0; d < ot->methods.GetLength(); d++ ) + { + derivedFunc = GetFunctionDescription(ot->methods[d]); + if( baseFunc->name == "opConv" || baseFunc->name == "opImplConv" || + baseFunc->name == "opCast" || baseFunc->name == "opImplCast" ) + { + // For the opConv and opCast methods, the return type can differ if they are different methods + if( derivedFunc->name == baseFunc->name && + derivedFunc->IsSignatureExceptNameEqual(baseFunc) ) + { + if( baseFunc->IsFinal() ) + { + asCString msg; + msg.Format(TXT_METHOD_CANNOT_OVERRIDE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } + + // Move the function from the methods array to the virtualFunctionTable + ot->methods.RemoveIndex(d); + ot->virtualFunctionTable.PushLast(derivedFunc); + found = true; + break; + } + } + else + { + if( derivedFunc->name == baseFunc->name && + derivedFunc->IsSignatureExceptNameAndReturnTypeEqual(baseFunc) ) + { + if( baseFunc->returnType != derivedFunc->returnType ) + { + asCString msg; + msg.Format(TXT_DERIVED_METHOD_MUST_HAVE_SAME_RETTYPE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } + + if( baseFunc->IsFinal() ) + { + asCString msg; + msg.Format(TXT_METHOD_CANNOT_OVERRIDE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } + + // Move the function from the methods array to the virtualFunctionTable + ot->methods.RemoveIndex(d); + ot->virtualFunctionTable.PushLast(derivedFunc); + found = true; + break; + } + } + } + + if( !found ) + { + // Push the base class function on the virtual function table + ot->virtualFunctionTable.PushLast(baseType->virtualFunctionTable[m]); + baseType->virtualFunctionTable[m]->AddRefInternal(); + + CheckForConflictsDueToDefaultArgs(decl->script, decl->node, baseType->virtualFunctionTable[m], ot); + } + + ot->methods.PushLast(baseType->methods[m]); + engine->scriptFunctions[baseType->methods[m]]->AddRefInternal(); + } + } + + if( !decl->isExistingShared ) + { + // Move this class' methods into the virtual function table + for( asUINT m = 0; m < ot->methods.GetLength(); m++ ) + { + asCScriptFunction *func = GetFunctionDescription(ot->methods[m]); + if( func->funcType != asFUNC_VIRTUAL ) + { + // Move the reference from the method list to the virtual function list + ot->methods.RemoveIndex(m); + ot->virtualFunctionTable.PushLast(func); + + // Substitute the function description in the method list for a virtual method + // Make sure the methods are in the same order as the virtual function table + ot->methods.PushLast(CreateVirtualFunction(func, (int)ot->virtualFunctionTable.GetLength() - 1)); + m--; + } + } + + // Make virtual function table chunks for each implemented interface + for( asUINT m = 0; m < ot->interfaces.GetLength(); m++ ) + { + asCObjectType *intf = ot->interfaces[m]; + + // Add all the interface's functions to the virtual function table + asUINT offset = asUINT(ot->virtualFunctionTable.GetLength()); + ot->interfaceVFTOffsets.PushLast(offset); + + for( asUINT j = 0; j < intf->methods.GetLength(); j++ ) + { + asCScriptFunction *intfFunc = GetFunctionDescription(intf->methods[j]); + + // Only create the table for functions that are explicitly from this interface, + // inherited interface methods will be put in that interface's table. + if( intfFunc->objectType != intf ) + continue; + + asASSERT((asUINT)intfFunc->vfTableIdx == j); + + //Find the interface function in the list of methods + asCScriptFunction *realFunc = 0; + for( asUINT p = 0; p < ot->methods.GetLength(); p++ ) + { + asCScriptFunction *func = GetFunctionDescription(ot->methods[p]); + + if( func->signatureId == intfFunc->signatureId ) + { + if( func->funcType == asFUNC_VIRTUAL ) + { + realFunc = ot->virtualFunctionTable[func->vfTableIdx]; + } + else + { + // This should not happen, all methods were moved into the virtual table + asASSERT(false); + } + break; + } + } + + // If realFunc is still null, the interface was not + // implemented and we error out later in the checks. + ot->virtualFunctionTable.PushLast(realFunc); + if( realFunc ) + realFunc->AddRefInternal(); + } + } + } + + // Enumerate each of the declared properties + asCScriptNode *node = decl->node->firstChild->next; + + // Skip list of classes and interfaces + while( node && node->nodeType == snIdentifier ) + node = node->next; + + while( node ) + { + if( node->nodeType == snDeclaration ) + { + asCScriptNode *nd = node->firstChild; + + // Is the property declared as private or protected? + bool isPrivate = false, isProtected = false; + if( nd && nd->tokenType == ttPrivate ) + { + isPrivate = true; + nd = nd->next; + } + else if( nd && nd->tokenType == ttProtected ) + { + isProtected = true; + nd = nd->next; + } + + // Determine the type of the property + asCScriptCode *file = decl->script; + asCDataType dt = CreateDataTypeFromNode(nd, file, ot->nameSpace, false, ot); + if( ot->IsShared() && dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf()); + WriteError(msg, file, node); + } + + if( dt.IsReadOnly() ) + WriteError(TXT_PROPERTY_CANT_BE_CONST, file, node); + + // Multiple properties can be declared separated by , + nd = nd->next; + while( nd ) + { + asCString name(&file->code[nd->tokenPos], nd->tokenLength); + + if( !decl->isExistingShared ) + { + CheckNameConflictMember(ot, name.AddressOf(), nd, file, true); + AddPropertyToClass(decl, name, dt, isPrivate, isProtected, false, file, nd); + } + else + { + // Verify that the property exists in the original declaration + bool found = false; + for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) + { + asCObjectProperty *prop = ot->properties[p]; + if( prop->isPrivate == isPrivate && + prop->isProtected == isProtected && + prop->name == name && + prop->type.IsEqualExceptRef(dt) ) + { + found = true; + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, ot->GetName()); + WriteError(str, file, nd); + } + } + + // Skip the initialization node + if( nd->next && nd->next->nodeType != snIdentifier ) + nd = nd->next; + + nd = nd->next; + } + } + else + asASSERT(false); + + node = node->next; + } + + // Add properties from included mixin classes that don't conflict with existing properties + IncludePropertiesFromMixins(decl); + + if( !decl->isExistingShared ) + toValidate.PushLast(decl); + + asASSERT( ot->interfaces.GetLength() == ot->interfaceVFTOffsets.GetLength() ); + } + + // TODO: Warn if a method overrides a base method without marking it as 'override'. + // It must be possible to turn off this warning through engine property. + + // TODO: A base class should be able to mark a method as 'abstract'. This will + // allow a base class to provide a partial implementation, but still force + // derived classes to implement specific methods. + + // Verify that all interface methods are implemented in the classes + // We do this here so the base class' methods have already been inherited + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + if( decl->isExistingShared ) continue; + + asCObjectType *ot = CastToObjectType(decl->typeInfo); + asCArray overrideValidations(ot->GetMethodCount()); + for( asUINT k = 0; k < ot->methods.GetLength(); k++ ) + overrideValidations.PushLast( !static_cast(ot->GetMethodByIndex(k, false))->IsOverride() ); + + for( asUINT m = 0; m < ot->interfaces.GetLength(); m++ ) + { + asCObjectType *objType = ot->interfaces[m]; + for( asUINT i = 0; i < objType->methods.GetLength(); i++ ) + { + // Only check the interface methods that was explicitly declared in this interface + // Methods that was inherited from other interfaces will be checked in those interfaces + if( objType != engine->scriptFunctions[objType->methods[i]]->objectType ) + continue; + + asUINT overrideIndex; + if( !DoesMethodExist(ot, objType->methods[i], &overrideIndex) ) + { + asCString str; + str.Format(TXT_MISSING_IMPLEMENTATION_OF_s, + engine->GetFunctionDeclaration(objType->methods[i]).AddressOf()); + WriteError(str, decl->script, decl->node); + } + else + overrideValidations[overrideIndex] = true; + } + } + + bool hasBaseClass = ot->derivedFrom != 0; + + for( asUINT j = 0; j < overrideValidations.GetLength(); j++ ) + { + if( !overrideValidations[j] && (!hasBaseClass || !DoesMethodExist(ot->derivedFrom, ot->methods[j])) ) + { + asCString msg; + msg.Format(TXT_METHOD_s_DOES_NOT_OVERRIDE, ot->GetMethodByIndex(j, false)->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } + } + } + + // Verify that the declared structures are valid, e.g. that the structure + // doesn't contain a member of its own type directly or indirectly + while( toValidate.GetLength() > 0 ) + { + asUINT numClasses = (asUINT)toValidate.GetLength(); + + asCArray toValidateNext((int)toValidate.GetLength()); + while( toValidate.GetLength() > 0 ) + { + sClassDeclaration *decl = toValidate[toValidate.GetLength()-1]; + asCObjectType *ot = CastToObjectType(decl->typeInfo); + int validState = 1; + for( n = 0; n < ot->properties.GetLength(); n++ ) + { + // A valid structure is one that uses only primitives or other valid objects + asCObjectProperty *prop = ot->properties[n]; + asCDataType dt = prop->type; + + // TODO: Add this check again, once solving the issues commented below + /* + if( dt.IsTemplate() ) + { + // TODO: This must verify all sub types, not just the first one + // TODO: Just because the subtype is not a handle doesn't mean the template will actually instance the object + // this it shouldn't automatically raise an error for this, e.g. weakref should be legal as member + // of the Object class + asCDataType sub = dt; + while( sub.IsTemplate() && !sub.IsObjectHandle() ) + sub = sub.GetSubType(); + + dt = sub; + } + */ + + if( dt.IsObject() && !dt.IsObjectHandle() ) + { + // Find the class declaration + sClassDeclaration *pdecl = 0; + for( asUINT p = 0; p < classDeclarations.GetLength(); p++ ) + { + if( classDeclarations[p]->typeInfo == dt.GetTypeInfo() ) + { + pdecl = classDeclarations[p]; + break; + } + } + + if( pdecl ) + { + if( pdecl->typeInfo == decl->typeInfo ) + { + WriteError(TXT_ILLEGAL_MEMBER_TYPE, decl->script, decl->node); + validState = 2; + break; + } + else if( pdecl->validState != 1 ) + { + validState = pdecl->validState; + break; + } + } + } + } + + if( validState == 1 ) + { + decl->validState = 1; + toValidate.PopLast(); + } + else if( validState == 2 ) + { + decl->validState = 2; + toValidate.PopLast(); + } + else + { + toValidateNext.PushLast(toValidate.PopLast()); + } + } + + toValidate = toValidateNext; + toValidateNext.SetLength(0); + + if( numClasses == toValidate.GetLength() ) + { + WriteError(TXT_ILLEGAL_MEMBER_TYPE, toValidate[0]->script, toValidate[0]->node); + break; + } + } + + if( numErrors > 0 ) return; + + // Verify which script classes can really form circular references, and mark only those as garbage collected. + // This must be done in the correct order, so that a class that contains another class isn't needlessly marked + // as garbage collected, just because the contained class was evaluated afterwards. + + // TODO: runtime optimize: This algorithm can be further improved by checking the types that inherits from + // a base class. If the base class is not shared all the classes that derive from it + // are known at compile time, and can thus be checked for potential circular references too. + // + // Observe, that doing this would conflict with another potential future feature, which is to + // allow incremental builds, i.e. allow application to add or replace classes in an + // existing module. However, the applications that want to use that should use a special + // build flag to not finalize the module. + + asCArray typesToValidate; + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + // Existing shared classes won't need evaluating, nor interfaces + sClassDeclaration *decl = classDeclarations[n]; + if( decl->isExistingShared ) continue; + + asCObjectType *ot = CastToObjectType(decl->typeInfo); + if( ot->IsInterface() ) continue; + + typesToValidate.PushLast(ot); + } + + asUINT numReevaluations = 0; + while( typesToValidate.GetLength() ) + { + if( numReevaluations > typesToValidate.GetLength() ) + { + // No types could be completely evaluated in the last iteration so + // we consider the remaining types in the array as garbage collected + break; + } + + asCObjectType *type = typesToValidate[0]; + typesToValidate.RemoveIndex(0); + + // If the type inherits from another type that is yet to be validated, then reinsert it at the end + if( type->derivedFrom && typesToValidate.Exists(type->derivedFrom) ) + { + typesToValidate.PushLast(type); + numReevaluations++; + continue; + } + + // If the type inherits from a known garbage collected type, then this type must also be garbage collected + if( type->derivedFrom && (type->derivedFrom->flags & asOBJ_GC) ) + { + type->flags |= asOBJ_GC; + continue; + } + + // Evaluate template instances (silently) before verifying each of the classes, since it is possible that + // a class will be marked as non-garbage collected, which in turn will mark the template instance that uses + // it as non-garbage collected, which in turn means the class that contains the array also do not have to be + // garbage collected + EvaluateTemplateInstances(numTempl, true); + + // Is there some path in which this structure is involved in circular references? + // If the type contains a member of a type that is yet to be validated, then reinsert it at the end + bool mustReevaluate = false; + bool gc = false; + for( asUINT p = 0; p < type->properties.GetLength(); p++ ) + { + asCDataType dt = type->properties[p]->type; + + if (dt.IsFuncdef()) + { + // If a class holds a function pointer as member then the class must be garbage collected as the + // function pointer can form circular references with the class through use of a delegate. Example: + // + // class A { B @b; void f(); } + // class B { F @f; } + // funcdef void F(); + // + // A a; + // @a.b = B(); // instance of A refers to instance of B + // @a.b.f = F(a.f); // instance of B refers to delegate that refers to instance of A + // + gc = true; + break; + } + + if( !dt.IsObject() ) + continue; + + if( typesToValidate.Exists(CastToObjectType(dt.GetTypeInfo())) ) + mustReevaluate = true; + else + { + if( dt.IsTemplate() ) + { + // Check if any of the subtypes are yet to be evaluated + bool skip = false; + for( asUINT s = 0; s < dt.GetTypeInfo()->GetSubTypeCount(); s++ ) + { + asCObjectType *t = reinterpret_cast(dt.GetTypeInfo()->GetSubType(s)); + if( typesToValidate.Exists(t) ) + { + mustReevaluate = true; + skip = true; + break; + } + } + if( skip ) + continue; + } + + if( dt.IsObjectHandle() ) + { + // If it is known that the handle can't be involved in a circular reference + // then this object doesn't need to be marked as garbage collected. + asCObjectType *prop = CastToObjectType(dt.GetTypeInfo()); + + if( prop->flags & asOBJ_SCRIPT_OBJECT ) + { + // For script objects, treat non-final classes as if they can contain references + // as it is not known what derived classes might do. For final types, check all + // properties to determine if any of those can cause a circular reference with this + // class. + if( prop->flags & asOBJ_NOINHERIT ) + { + for( asUINT sp = 0; sp < prop->properties.GetLength(); sp++ ) + { + asCDataType sdt = prop->properties[sp]->type; + + if( sdt.IsObject() ) + { + if( sdt.IsObjectHandle() ) + { + // TODO: runtime optimize: If the handle is again to a final class, then we can recursively check if the circular reference can occur + if( sdt.GetTypeInfo()->flags & (asOBJ_SCRIPT_OBJECT | asOBJ_GC) ) + { + gc = true; + break; + } + } + else if( sdt.GetTypeInfo()->flags & asOBJ_GC ) + { + // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. + // Only if the object is of a type that can reference this type, either directly or indirectly + gc = true; + break; + } + } + } + + if( gc ) + break; + } + else + { + // Assume it is garbage collected as it is not known at compile time what might inherit from this type + gc = true; + break; + } + } + else if( prop->flags & asOBJ_GC ) + { + // If a type is not a script object, adopt its GC flag + // TODO: runtime optimize: Just because an application registered class is garbage collected, doesn't mean it + // can form a circular reference with this script class. Perhaps need a flag to tell + // if the script classes that contains the type should be garbage collected or not. + gc = true; + break; + } + } + else if( dt.GetTypeInfo()->flags & asOBJ_GC ) + { + // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. + // Only if the object is of a type that can reference this type, either directly or indirectly + gc = true; + break; + } + } + } + + // If the class wasn't found to require garbage collection, but it + // contains another type that has yet to be evaluated then it must be + // re-evaluated. + if( !gc && mustReevaluate ) + { + typesToValidate.PushLast(type); + numReevaluations++; + continue; + } + + // Update the flag in the object type + if( gc ) + type->flags |= asOBJ_GC; + else + type->flags &= ~asOBJ_GC; + + // Reset the counter + numReevaluations = 0; + } +} + +void asCBuilder::IncludeMethodsFromMixins(sClassDeclaration *decl) +{ + asCScriptNode *node = decl->node->firstChild; + + // Skip the class attributes + while( node->nodeType == snIdentifier && + !decl->script->TokenEquals(node->tokenPos, node->tokenLength, decl->name.AddressOf()) ) + node = node->next; + + // Skip the name of the class + node = node->next; + + // Find the included mixin classes + while( node && node->nodeType == snIdentifier ) + { + asSNameSpace *ns; + asCString name; + if( GetNamespaceAndNameFromNode(node, decl->script, decl->typeInfo->nameSpace, ns, name) < 0 ) + { + node = node->next; + continue; + } + + sMixinClass *mixin = 0; + while( ns ) + { + // Need to make sure the name is not an object type + asCObjectType *objType = GetObjectType(name.AddressOf(), ns); + if( objType == 0 ) + mixin = GetMixinClass(name.AddressOf(), ns); + + if( objType || mixin ) + break; + + ns = engine->GetParentNameSpace(ns); + } + + if( mixin ) + { + // Find methods from mixin declaration + asCScriptNode *n = mixin->node->firstChild; + + // Skip to the member declarations + // Possible keywords 'final' and 'shared' are removed in RegisterMixinClass so we don't need to worry about those here + while( n && n->nodeType == snIdentifier ) + n = n->next; + + // Add methods from the mixin that are not already existing in the class + while( n ) + { + if( n->nodeType == snFunction ) + { + // Instead of disconnecting the node, we need to clone it, otherwise other + // classes that include the same mixin will not see the methods + asCScriptNode *copy = n->CreateCopy(engine); + + // Register the method, but only if it doesn't already exist in the class + RegisterScriptFunctionFromNode(copy, mixin->script, CastToObjectType(decl->typeInfo), false, false, mixin->ns, false, true); + } + else if( n->nodeType == snVirtualProperty ) + { + // TODO: mixin: Support virtual properties too + WriteError("The virtual property syntax is currently not supported for mixin classes", mixin->script, n); + //RegisterVirtualProperty(node, decl->script, decl->objType, false, false); + } + + n = n->next; + } + } + + node = node->next; + } +} + +void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) +{ + asCScriptNode *node = decl->node->firstChild; + + // Skip the class attributes + while( node->nodeType == snIdentifier && + !decl->script->TokenEquals(node->tokenPos, node->tokenLength, decl->name.AddressOf()) ) + node = node->next; + + // Skip the name of the class + node = node->next; + + // Find the included mixin classes + while( node && node->nodeType == snIdentifier ) + { + asSNameSpace *ns; + asCString name; + if( GetNamespaceAndNameFromNode(node, decl->script, decl->typeInfo->nameSpace, ns, name) < 0 ) + { + node = node->next; + continue; + } + + sMixinClass *mixin = 0; + while( ns ) + { + // Need to make sure the name is not an object type + asCObjectType *objType = GetObjectType(name.AddressOf(), ns); + if( objType == 0 ) + mixin = GetMixinClass(name.AddressOf(), ns); + + if( objType || mixin ) + break; + + ns = engine->GetParentNameSpace(ns); + } + + if( mixin ) + { + // Find properties from mixin declaration + asCScriptNode *n = mixin->node->firstChild; + + // Skip to the member declarations + // Possible keywords 'final' and 'shared' are removed in RegisterMixinClass so we don't need to worry about those here + while( n && n->nodeType == snIdentifier ) + n = n->next; + + // Add properties from the mixin that are not already existing in the class + while( n ) + { + if( n->nodeType == snDeclaration ) + { + asCScriptNode *n2 = n->firstChild; + bool isPrivate = false, isProtected = false; + if( n2 && n2->tokenType == ttPrivate ) + { + isPrivate = true; + n2 = n2->next; + } + else if( n2 && n2->tokenType == ttProtected ) + { + isProtected = true; + n2 = n2->next; + } + + asCScriptCode *file = mixin->script; + asCDataType dt = CreateDataTypeFromNode(n2, file, mixin->ns); + + if( decl->typeInfo->IsShared() && dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf()); + WriteError(msg, file, n); + WriteInfo(TXT_WHILE_INCLUDING_MIXIN, decl->script, node); + } + + if( dt.IsReadOnly() ) + WriteError(TXT_PROPERTY_CANT_BE_CONST, file, n); + + n2 = n2->next; + while( n2 ) + { + name.Assign(&file->code[n2->tokenPos], n2->tokenLength); + + // Add the property only if it doesn't already exist in the class + bool exists = false; + asCObjectType *ot = CastToObjectType(decl->typeInfo); + for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) + if( ot->properties[p]->name == name ) + { + exists = true; + break; + } + + if( !exists ) + { + if( !decl->isExistingShared ) + { + // It must not conflict with the name of methods + int r = CheckNameConflictMember(ot, name.AddressOf(), n2, file, true); + if( r < 0 ) + WriteInfo(TXT_WHILE_INCLUDING_MIXIN, decl->script, node); + + AddPropertyToClass(decl, name, dt, isPrivate, isProtected, false, file, n2); + } + else + { + // Verify that the property exists in the original declaration + bool found = false; + for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) + { + asCObjectProperty *prop = ot->properties[p]; + if( prop->isPrivate == isPrivate && + prop->isProtected == isProtected && + prop->name == name && + prop->type == dt ) + { + found = true; + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, ot->GetName()); + WriteError(str, decl->script, decl->node); + WriteInfo(TXT_WHILE_INCLUDING_MIXIN, decl->script, node); + } + } + } + + // Skip the initialization expression + if( n2->next && n2->next->nodeType != snIdentifier ) + n2 = n2->next; + + n2 = n2->next; + } + } + + n = n->next; + } + } + + node = node->next; + } +} + +int asCBuilder::CreateVirtualFunction(asCScriptFunction *func, int idx) +{ + asCScriptFunction *vf = asNEW(asCScriptFunction)(engine, module, asFUNC_VIRTUAL); + if( vf == 0 ) + return asOUT_OF_MEMORY; + + vf->name = func->name; + vf->returnType = func->returnType; + vf->parameterTypes = func->parameterTypes; + vf->inOutFlags = func->inOutFlags; + vf->id = engine->GetNextScriptFunctionId(); + vf->isReadOnly = func->isReadOnly; + vf->objectType = func->objectType; + vf->objectType->AddRefInternal(); + vf->signatureId = func->signatureId; + vf->isPrivate = func->isPrivate; + vf->isProtected = func->isProtected; + vf->isFinal = func->isFinal; + vf->isOverride = func->isOverride; + vf->vfTableIdx = idx; + + // It is not necessary to copy the default args, as they have no meaning in the virtual function + + module->AddScriptFunction(vf); + + // Add a dummy to the builder so that it doesn't mix up function ids + functions.PushLast(0); + + return vf->id; +} + +asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const asCString &name, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited, asCScriptCode *file, asCScriptNode *node) +{ + if( node ) + { + asASSERT(!isInherited); + + // Check if the property is allowed + if( !dt.CanBeInstantiated() ) + { + if( file && node ) + { + asCString str; + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(decl->typeInfo->nameSpace).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(decl->typeInfo->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(decl->typeInfo->nameSpace).AddressOf()); + WriteError(str, file, node); + } + return 0; + } + + // Register the initialization expression (if any) to be compiled later + asCScriptNode *declNode = node; + asCScriptNode *initNode = 0; + if( node->next && node->next->nodeType != snIdentifier ) + { + asASSERT( node->next->nodeType == snAssignment ); + initNode = node->next; + } + + sPropertyInitializer p(name, declNode, initNode, file); + decl->propInits.PushLast(p); + } + else + { + // If the declaration node is not given, then + // this property is inherited from a base class + asASSERT(isInherited); + } + + // Add the property to the object type + return CastToObjectType(decl->typeInfo)->AddPropertyToClass(name, dt, isPrivate, isProtected, isInherited); +} + +bool asCBuilder::DoesMethodExist(asCObjectType *objType, int methodId, asUINT *methodIndex) +{ + asCScriptFunction *method = GetFunctionDescription(methodId); + + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *m = GetFunctionDescription(objType->methods[n]); + + if( m->name != method->name ) continue; + if( m->returnType != method->returnType ) continue; + if( m->isReadOnly != method->isReadOnly ) continue; + if( m->parameterTypes != method->parameterTypes ) continue; + if( m->inOutFlags != method->inOutFlags ) continue; + + if( methodIndex ) + *methodIndex = n; + + return true; + } + + return false; +} + +void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file) +{ + int funcId = engine->GetNextScriptFunctionId(); + + asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false); + asCArray parameterTypes; + asCArray inOutFlags; + asCArray defaultArgs; + asCArray parameterNames; + + // Add the script function + // TODO: declaredAt should be set to where the class has been declared + module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, false, objType, false, false, false, false, false, false, false, objType->nameSpace); + + // Set it as default constructor + if( objType->beh.construct ) + engine->scriptFunctions[objType->beh.construct]->ReleaseInternal(); + objType->beh.construct = funcId; + objType->beh.constructors[0] = funcId; + engine->scriptFunctions[funcId]->AddRefInternal(); + + // The bytecode for the default constructor will be generated + // only after the potential inheritance has been established + sFunctionDescription *func = asNEW(sFunctionDescription); + if( func == 0 ) + { + // Out of memory + return; + } + + functions.PushLast(func); + + func->script = file; + func->node = 0; + func->name = objType->name; + func->objType = objType; + func->funcId = funcId; + func->isExistingShared = false; + + // Add a default factory as well + funcId = engine->GetNextScriptFunctionId(); + if( objType->beh.factory ) + engine->scriptFunctions[objType->beh.factory]->ReleaseInternal(); + objType->beh.factory = funcId; + objType->beh.factories[0] = funcId; + returnType = asCDataType::CreateObjectHandle(objType, false); + // TODO: should be the same as the constructor + module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, false); + functions.PushLast(0); + asCCompiler compiler(engine); + compiler.CompileFactory(this, file, engine->scriptFunctions[funcId]); + engine->scriptFunctions[funcId]->AddRefInternal(); + + // If the object is shared, then the factory must also be marked as shared + if( objType->flags & asOBJ_SHARED ) + engine->scriptFunctions[funcId]->isShared = true; +} + +int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + // Is it a shared enum? + bool isShared = false; + asCEnumType *existingSharedType = 0; + asCScriptNode *tmp = node->firstChild; + if( tmp->nodeType == snIdentifier && file->TokenEquals(tmp->tokenPos, tmp->tokenLength, SHARED_TOKEN) ) + { + isShared = true; + tmp = tmp->next; + } + + // Grab the name of the enumeration + asCString name; + asASSERT(snDataType == tmp->nodeType); + asASSERT(snIdentifier == tmp->firstChild->nodeType); + name.Assign(&file->code[tmp->firstChild->tokenPos], tmp->firstChild->tokenLength); + + if( isShared ) + { + // Look for a pre-existing shared enum with the same signature + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) + { + asCTypeInfo *o = engine->sharedScriptTypes[n]; + if( o && + o->IsShared() && + (o->flags & asOBJ_ENUM) && + o->name == name && + o->nameSpace == ns ) + { + existingSharedType = CastToEnumType(o); + break; + } + } + } + + // Check the name and add the enum + int r = CheckNameConflict(name.AddressOf(), tmp->firstChild, file, ns); + if( asSUCCESS == r ) + { + asCEnumType *st; + + if( existingSharedType ) + { + st = existingSharedType; + st->AddRefInternal(); + } + else + { + st = asNEW(asCEnumType)(engine); + if( st == 0 ) + return asOUT_OF_MEMORY; + + st->flags = asOBJ_ENUM; + if( isShared ) + st->flags |= asOBJ_SHARED; + st->size = 4; + st->name = name; + st->nameSpace = ns; + st->module = module; + } + module->enumTypes.PushLast(st); + + if( !existingSharedType && isShared ) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } + + // Store the location of this declaration for reference in name collisions + sClassDeclaration *decl = asNEW(sClassDeclaration); + if( decl == 0 ) + return asOUT_OF_MEMORY; + + decl->name = name; + decl->script = file; + decl->typeInfo = st; + namedTypeDeclarations.PushLast(decl); + + asCDataType type = CreateDataTypeFromNode(tmp, file, ns); + asASSERT(!type.IsReference()); + + // Register the enum values + tmp = tmp->next; + while( tmp ) + { + asASSERT(snIdentifier == tmp->nodeType); + + name.Assign(&file->code[tmp->tokenPos], tmp->tokenLength); + + if( existingSharedType ) + { + // If this is a pre-existent shared enum, then just double check + // that the value is already defined in the original declaration + bool found = false; + for( asUINT n = 0; n < st->enumValues.GetLength(); n++ ) + if( st->enumValues[n]->name == name ) + { + found = true; + break; + } + + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, st->GetName()); + WriteError(str, file, tmp); + break; + } + + tmp = tmp->next; + if( tmp && tmp->nodeType == snAssignment ) + tmp = tmp->next; + continue; + } + else + { + // Check for name conflict errors with other values in the enum + if( globVariables.GetFirst(ns, name, asCCompGlobVarType(type)) ) + { + asCString str; + str.Format(TXT_NAME_CONFLICT_s_ALREADY_USED, name.AddressOf()); + WriteError(str, file, tmp); + + tmp = tmp->next; + if( tmp && tmp->nodeType == snAssignment ) + tmp = tmp->next; + continue; + } + + // Check for assignment + asCScriptNode *asnNode = tmp->next; + if( asnNode && snAssignment == asnNode->nodeType ) + asnNode->DisconnectParent(); + else + asnNode = 0; + + // Create the global variable description so the enum value can be evaluated + sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription); + if( gvar == 0 ) + return asOUT_OF_MEMORY; + + gvar->script = file; + gvar->declaredAtNode = tmp; + tmp = tmp->next; + gvar->declaredAtNode->DisconnectParent(); + gvar->initializationNode = asnNode; + gvar->name = name; + gvar->datatype = type; + gvar->ns = ns; + // No need to allocate space on the global memory stack since the values are stored in the asCObjectType + // Set the index to a negative to allow compiler to diferentiate from ordinary global var when compiling the initialization + gvar->index = -1; + gvar->isCompiled = false; + gvar->isPureConstant = true; + gvar->isEnumValue = true; + gvar->constantValue = 0xdeadbeef; + + // Allocate dummy property so we can compile the value. + // This will be removed later on so we don't add it to the engine. + gvar->property = asNEW(asCGlobalProperty); + if( gvar->property == 0 ) + return asOUT_OF_MEMORY; + + gvar->property->name = name; + gvar->property->nameSpace = ns; + gvar->property->type = gvar->datatype; + gvar->property->id = 0; + + globVariables.Put(gvar); + } + } + } + + node->Destroy(engine); + + return r; +} + +int asCBuilder::RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + // Get the native data type + asCScriptNode *tmp = node->firstChild; + asASSERT(NULL != tmp && snDataType == tmp->nodeType); + asCDataType dataType; + dataType.CreatePrimitive(tmp->tokenType, false); + dataType.SetTokenType(tmp->tokenType); + tmp = tmp->next; + + // Grab the name of the typedef + asASSERT(NULL != tmp && NULL == tmp->next); + asCString name; + name.Assign(&file->code[tmp->tokenPos], tmp->tokenLength); + + // If the name is not already in use add it + int r = CheckNameConflict(name.AddressOf(), tmp, file, ns); + + asCTypedefType *st = 0; + if( asSUCCESS == r ) + { + // Create the new type + st = asNEW(asCTypedefType)(engine); + if( st == 0 ) + r = asOUT_OF_MEMORY; + } + + if( asSUCCESS == r ) + { + st->flags = asOBJ_TYPEDEF; + st->size = dataType.GetSizeInMemoryBytes(); + st->name = name; + st->nameSpace = ns; + st->aliasForType = dataType; + st->module = module; + + module->typeDefs.PushLast(st); + + // Store the location of this declaration for reference in name collisions + sClassDeclaration *decl = asNEW(sClassDeclaration); + if( decl == 0 ) + r = asOUT_OF_MEMORY; + else + { + decl->name = name; + decl->script = file; + decl->typeInfo = st; + namedTypeDeclarations.PushLast(decl); + } + } + + node->Destroy(engine); + + return r; +} + +void asCBuilder::GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isProtected, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace) +{ + node = node->firstChild; + + // Is the function a private or protected class method? + isPrivate = false, isProtected = false; + if( node->tokenType == ttPrivate ) + { + isPrivate = true; + node = node->next; + } + else if( node->tokenType == ttProtected ) + { + isProtected = true; + node = node->next; + } + + // Is the function shared? + isShared = false; + if( node->tokenType == ttIdentifier && file->TokenEquals(node->tokenPos, node->tokenLength, SHARED_TOKEN) ) + { + isShared = true; + node = node->next; + } + + // Find the name + isConstructor = false; + isDestructor = false; + asCScriptNode *n = 0; + if( node->nodeType == snDataType ) + n = node->next->next; + else + { + // If the first node is a ~ token, then we know it is a destructor + if( node->tokenType == ttBitNot ) + { + n = node->next; + isDestructor = true; + } + else + { + n = node; + isConstructor = true; + } + } + name.Assign(&file->code[n->tokenPos], n->tokenLength); + + // Initialize a script function object for registration + if( !isConstructor && !isDestructor ) + { + returnType = CreateDataTypeFromNode(node, file, implicitNamespace, false, objType); + returnType = ModifyDataTypeFromNode(returnType, node->next, file, 0, 0); + + if( engine->ep.disallowValueAssignForRefType && + returnType.GetTypeInfo() && + (returnType.GetTypeInfo()->flags & asOBJ_REF) && + !(returnType.GetTypeInfo()->flags & asOBJ_SCOPED) && + !returnType.IsReference() && + !returnType.IsObjectHandle() ) + { + WriteError(TXT_REF_TYPE_CANT_BE_RETURNED_BY_VAL, file, node); + } + } + else + returnType = asCDataType::CreatePrimitive(ttVoid, false); + + isConstMethod = false; + isFinal = false; + isOverride = false; + + if( objType && n->next->next ) + { + asCScriptNode *decorator = n->next->next; + + // Is this a const method? + if( decorator->tokenType == ttConst ) + { + isConstMethod = true; + decorator = decorator->next; + } + + while( decorator ) + { + if( decorator->tokenType == ttIdentifier && file->TokenEquals(decorator->tokenPos, decorator->tokenLength, FINAL_TOKEN) ) + isFinal = true; + else if( decorator->tokenType == ttIdentifier && file->TokenEquals(decorator->tokenPos, decorator->tokenLength, OVERRIDE_TOKEN) ) + isOverride = true; + + decorator = decorator->next; + } + } + + // Count the number of parameters + int count = 0; + asCScriptNode *c = n->next->firstChild; + while( c ) + { + count++; + c = c->next->next; + if( c && c->nodeType == snIdentifier ) + c = c->next; + if( c && c->nodeType == snExpression ) + c = c->next; + } + + // Get the parameter types + parameterNames.Allocate(count, false); + parameterTypes.Allocate(count, false); + inOutFlags.Allocate(count, false); + defaultArgs.Allocate(count, false); + n = n->next->firstChild; + while( n ) + { + asETypeModifiers inOutFlag; + asCDataType type = CreateDataTypeFromNode(n, file, implicitNamespace, false, objType); + type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0); + + if( engine->ep.disallowValueAssignForRefType && + type.GetTypeInfo() && + (type.GetTypeInfo()->flags & asOBJ_REF) && + !(type.GetTypeInfo()->flags & asOBJ_SCOPED) && + !type.IsReference() && + !type.IsObjectHandle() ) + { + WriteError(TXT_REF_TYPE_CANT_BE_PASSED_BY_VAL, file, node); + } + + // Store the parameter type + parameterTypes.PushLast(type); + inOutFlags.PushLast(inOutFlag); + + // Move to next parameter + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + { + asCString paramName(&file->code[n->tokenPos], n->tokenLength); + parameterNames.PushLast(paramName); + n = n->next; + } + else + { + // No name was given for the parameter + parameterNames.PushLast(asCString()); + } + + if( n && n->nodeType == snExpression ) + { + // Strip out white space and comments to better share the string + asCString *defaultArgStr = asNEW(asCString); + if( defaultArgStr ) + *defaultArgStr = GetCleanExpressionString(n, file); + defaultArgs.PushLast(defaultArgStr); + + n = n->next; + } + else + defaultArgs.PushLast(0); + } +} +#endif + +asCString asCBuilder::GetCleanExpressionString(asCScriptNode *node, asCScriptCode *file) +{ + asASSERT(node && node->nodeType == snExpression); + + asCString str; + str.Assign(file->code + node->tokenPos, node->tokenLength); + + asCString cleanStr; + for( asUINT n = 0; n < str.GetLength(); ) + { + asUINT len = 0; + asETokenClass tok = engine->ParseToken(str.AddressOf() + n, str.GetLength() - n, &len); + if( tok != asTC_COMMENT && tok != asTC_WHITESPACE ) + { + if( cleanStr.GetLength() ) cleanStr += " "; + cleanStr.Concatenate(str.AddressOf() + n, len); + } + n += len; + } + + return cleanStr; +} + +#ifndef AS_NO_COMPILER +int asCBuilder::RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin) +{ + asCString name; + asCDataType returnType; + asCArray parameterNames; + asCArray parameterTypes; + asCArray inOutFlags; + asCArray defaultArgs; + bool isConstMethod; + bool isOverride; + bool isFinal; + bool isConstructor; + bool isDestructor; + bool isPrivate; + bool isProtected; + bool isShared; + + asASSERT( (objType && ns == 0) || isGlobalFunction || isMixin ); + + // Set the default namespace + if( ns == 0 ) + { + if( objType ) + ns = objType->nameSpace; + else + ns = engine->nameSpaces[0]; + } + + GetParsedFunctionDetails(node, file, objType, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, ns); + + return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared); +} + +asCScriptFunction *asCBuilder::RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns) +{ + // Get the parameter names from the node + asCArray parameterNames; + asCArray defaultArgs; + asCScriptNode *args = node->firstChild; + while( args && args->nodeType == snIdentifier ) + { + asCString argName; + argName.Assign(&file->code[args->tokenPos], args->tokenLength); + parameterNames.PushLast(argName); + defaultArgs.PushLast(0); + args = args->next; + } + + // The statement block for the function must be disconnected, as the builder is going to be the owner of it + args->DisconnectParent(); + + // Get the return and parameter types from the funcDef + asCString funcName = name; + int r = RegisterScriptFunction(args, file, 0, 0, true, ns, false, false, funcName, funcDef->returnType, parameterNames, funcDef->parameterTypes, funcDef->inOutFlags, defaultArgs, false, false, false, false, false, false, false, false); + if( r < 0 ) + return 0; + + // Return the function that was just created (but that will be compiled later) + return engine->scriptFunctions[functions[functions.GetLength()-1]->funcId]; +} + +int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared) +{ + // Determine default namespace if not specified + if( ns == 0 ) + { + if( objType ) + ns = objType->nameSpace; + else + ns = engine->nameSpaces[0]; + } + + if( isExistingShared ) + { + asASSERT( objType ); + + // Should validate that the function really exists in the class/interface + bool found = false; + if( isConstructor || isDestructor ) + { + // TODO: shared: Should check the existance of these too + found = true; + } + else + { + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[objType->methods[n]]; + if( func->name == name && + func->IsSignatureExceptNameEqual(returnType, parameterTypes, inOutFlags, objType, isConstMethod) ) + { + // Add the shared function in this module too + module->AddScriptFunction(func); + + found = true; + break; + } + } + } + + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, objType->GetName()); + WriteError(str, file, node); + } + + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + node->Destroy(engine); + return 0; + } + + // Check for name conflicts + if( !isConstructor && !isDestructor ) + { + if( objType ) + { + CheckNameConflictMember(objType, name.AddressOf(), node, file, false); + + if( name == objType->name ) + WriteError(TXT_METHOD_CANT_HAVE_NAME_OF_CLASS, file, node); + } + else + CheckNameConflict(name.AddressOf(), node, file, ns); + } + else + { + if( isMixin ) + { + // Mixins cannot implement constructors/destructors + WriteError(TXT_MIXIN_CANNOT_HAVE_CONSTRUCTOR, file, node); + + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + node->Destroy(engine); + return 0; + } + + // Verify that the name of the constructor/destructor is the same as the class + if( name != objType->name ) + { + asCString str; + if( isDestructor ) + str.Format(TXT_DESTRUCTOR_s_s_NAME_ERROR, objType->name.AddressOf(), name.AddressOf()); + else + str.Format(TXT_METHOD_s_s_HAS_NO_RETURN_TYPE, objType->name.AddressOf(), name.AddressOf()); + WriteError(str, file, node); + } + + if( isDestructor ) + name = "~" + name; + } + + isExistingShared = false; + int funcId = engine->GetNextScriptFunctionId(); + if( !isInterface ) + { + sFunctionDescription *func = asNEW(sFunctionDescription); + if( func == 0 ) + { + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return asOUT_OF_MEMORY; + } + + functions.PushLast(func); + + func->script = file; + func->node = node; + func->name = name; + func->objType = objType; + func->funcId = funcId; + func->isExistingShared = false; + func->paramNames = parameterNames; + + if( isShared ) + { + // Look for a pre-existing shared function with the same signature + for( asUINT n = 0; n < engine->scriptFunctions.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[n]; + if( f && + f->isShared && + f->name == name && + f->nameSpace == ns && + f->objectType == objType && + f->IsSignatureExceptNameEqual(returnType, parameterTypes, inOutFlags, 0, false) ) + { + funcId = func->funcId = f->id; + isExistingShared = func->isExistingShared = true; + break; + } + } + } + } + + // Destructors may not have any parameters + if( isDestructor && parameterTypes.GetLength() > 0 ) + WriteError(TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM, file, node); + + // If a function, class, or interface is shared then only shared types may be used in the signature + if( (objType && objType->IsShared()) || isShared ) + { + asCTypeInfo *ti = returnType.GetTypeInfo(); + if( ti && !ti->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, ti->name.AddressOf()); + WriteError(msg, file, node); + } + + for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) + { + ti = parameterTypes[p].GetTypeInfo(); + if( ti && !ti->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, ti->name.AddressOf()); + WriteError(msg, file, node); + } + } + } + + // Check that the same function hasn't been registered already in the namespace + asCArray funcs; + if( objType ) + GetObjectMethodDescriptions(name.AddressOf(), objType, funcs, false); + else + GetFunctionDescriptions(name.AddressOf(), funcs, ns); + if( objType && (name == "opConv" || name == "opImplConv" || name == "opCast" || name == "opImplCast") && parameterTypes.GetLength() == 0 ) + { + // opConv and opCast are special methods used for type casts + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + if( func->IsSignatureExceptNameEqual(returnType, parameterTypes, inOutFlags, objType, isConstMethod) ) + { + // TODO: clean up: Reuse the same error handling for both opConv and normal methods + if( isMixin ) + { + // Clean up the memory, as the function will not be registered + if( node ) + node->Destroy(engine); + sFunctionDescription *funcDesc = functions.PopLast(); + asDELETE(funcDesc, sFunctionDescription); + + // Free the default args + for( n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return 0; + } + + WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); + break; + } + } + } + else + { + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + if( func->IsSignatureExceptNameAndReturnTypeEqual(parameterTypes, inOutFlags, objType, isConstMethod) ) + { + if( isMixin ) + { + // Clean up the memory, as the function will not be registered + if( node ) + node->Destroy(engine); + sFunctionDescription *funcDesc = functions.PopLast(); + asDELETE(funcDesc, sFunctionDescription); + + // Free the default args + for( n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return 0; + } + + WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); + break; + } + } + } + + // Register the function + if( isExistingShared ) + { + // Delete the default args as they won't be used anymore + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + asCScriptFunction *f = engine->scriptFunctions[funcId]; + module->AddScriptFunction(f); + + // TODO: clean up: This should be done by AddScriptFunction() itself + module->globalFunctions.Put(f); + } + else + { + int row = 0, col = 0; + if( node ) + file->ConvertPosToRowCol(node->tokenPos, &row, &col); + module->AddScriptFunction(file->idx, (row&0xFFFFF)|((col&0xFFF)<<20), funcId, name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, isInterface, objType, isConstMethod, isGlobalFunction, isPrivate, isProtected, isFinal, isOverride, isShared, ns); + } + + // Make sure the default args are declared correctly + ValidateDefaultArgs(file, node, engine->scriptFunctions[funcId]); + CheckForConflictsDueToDefaultArgs(file, node, engine->scriptFunctions[funcId], objType); + + if( objType ) + { + asASSERT( !isExistingShared ); + + engine->scriptFunctions[funcId]->AddRefInternal(); + if( isConstructor ) + { + int factoryId = engine->GetNextScriptFunctionId(); + if( parameterTypes.GetLength() == 0 ) + { + // Overload the default constructor + engine->scriptFunctions[objType->beh.construct]->ReleaseInternal(); + objType->beh.construct = funcId; + objType->beh.constructors[0] = funcId; + + // Register the default factory as well + engine->scriptFunctions[objType->beh.factory]->ReleaseInternal(); + objType->beh.factory = factoryId; + objType->beh.factories[0] = factoryId; + } + else + { + objType->beh.constructors.PushLast(funcId); + + // Register the factory as well + objType->beh.factories.PushLast(factoryId); + } + + // We must copy the default arg strings to avoid deleting the same object multiple times + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + defaultArgs[n] = asNEW(asCString)(*defaultArgs[n]); + + asCDataType dt = asCDataType::CreateObjectHandle(objType, false); + module->AddScriptFunction(file->idx, engine->scriptFunctions[funcId]->scriptData->declaredAt, factoryId, name, dt, parameterTypes, parameterNames, inOutFlags, defaultArgs, false); + + // If the object is shared, then the factory must also be marked as shared + if( objType->flags & asOBJ_SHARED ) + engine->scriptFunctions[factoryId]->isShared = true; + + // Add a dummy function to the builder so that it doesn't mix up the fund Ids + functions.PushLast(0); + + // Compile the factory immediately + asCCompiler compiler(engine); + compiler.CompileFactory(this, file, engine->scriptFunctions[factoryId]); + engine->scriptFunctions[factoryId]->AddRefInternal(); + } + else if( isDestructor ) + objType->beh.destruct = funcId; + else + { + // If the method is the assignment operator we need to replace the default implementation + asCScriptFunction *f = engine->scriptFunctions[funcId]; + if( f->name == "opAssign" && f->parameterTypes.GetLength() == 1 && + f->parameterTypes[0].GetTypeInfo() == f->objectType && + (f->inOutFlags[0] & asTM_INREF) ) + { + engine->scriptFunctions[objType->beh.copy]->ReleaseInternal(); + objType->beh.copy = funcId; + f->AddRefInternal(); + } + + objType->methods.PushLast(funcId); + } + } + + // We need to delete the node already if this is an interface method + if( isInterface && node ) + node->Destroy(engine); + + return 0; +} + +int asCBuilder::RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared) +{ + if( engine->ep.propertyAccessorMode != 2 ) + { + WriteError(TXT_PROPERTY_ACCESSOR_DISABLED, file, node); + node->Destroy(engine); + return 0; + } + + asASSERT( (objType && ns == 0) || isGlobalFunction ); + + if( ns == 0 ) + { + if( objType ) + ns = objType->nameSpace; + else + ns = engine->nameSpaces[0]; + } + + bool isPrivate = false, isProtected = false; + asCString emulatedName; + asCDataType emulatedType; + + asCScriptNode *mainNode = node; + node = node->firstChild; + + if( !isGlobalFunction && node->tokenType == ttPrivate ) + { + isPrivate = true; + node = node->next; + } + else if( !isGlobalFunction && node->tokenType == ttProtected ) + { + isProtected = true; + node = node->next; + } + + emulatedType = CreateDataTypeFromNode(node, file, ns); + emulatedType = ModifyDataTypeFromNode(emulatedType, node->next, file, 0, 0); + node = node->next->next; + emulatedName.Assign(&file->code[node->tokenPos], node->tokenLength); + + if( node->next == 0 ) + WriteError(TXT_PROPERTY_WITHOUT_ACCESSOR, file, node); + + node = node->next; + while( node ) + { + asCScriptNode *next = node->next; + asCScriptNode *funcNode = 0; + bool success = false; + bool isConst = false; + bool isFinal = false; + bool isOverride = false; + asCDataType returnType; + asCArray paramNames; + asCArray paramTypes; + asCArray paramModifiers; + asCArray defaultArgs; + asCString name; + + // TODO: getset: Allow private for individual property accessors + // TODO: getset: If the accessor uses its own name, then the property should be automatically declared + + if( node->firstChild->nodeType == snIdentifier && file->TokenEquals(node->firstChild->tokenPos, node->firstChild->tokenLength, GET_TOKEN) ) + { + funcNode = node->firstChild->next; + + if( funcNode && funcNode->tokenType == ttConst ) + { + isConst = true; + funcNode = funcNode->next; + } + + while( funcNode && funcNode->nodeType != snStatementBlock ) + { + if( funcNode->tokenType == ttIdentifier && file->TokenEquals(funcNode->tokenPos, funcNode->tokenLength, FINAL_TOKEN) ) + isFinal = true; + else if( funcNode->tokenType == ttIdentifier && file->TokenEquals(funcNode->tokenPos, funcNode->tokenLength, OVERRIDE_TOKEN) ) + isOverride = true; + + funcNode = funcNode->next; + } + + if( funcNode ) + funcNode->DisconnectParent(); + + if( funcNode == 0 && (objType == 0 || !objType->IsInterface()) ) + { + // TODO: getset: If no implementation is supplied the builder should provide an automatically generated implementation + // The compiler needs to be able to handle the different types, primitive, value type, and handle + // The code is also different for global property accessors + WriteError(TXT_PROPERTY_ACCESSOR_MUST_BE_IMPLEMENTED, file, node); + } + + // Setup the signature for the get accessor method + returnType = emulatedType; + name = "get_" + emulatedName; + success = true; + } + else if( node->firstChild->nodeType == snIdentifier && file->TokenEquals(node->firstChild->tokenPos, node->firstChild->tokenLength, SET_TOKEN) ) + { + funcNode = node->firstChild->next; + + if( funcNode && funcNode->tokenType == ttConst ) + { + isConst = true; + funcNode = funcNode->next; + } + + while( funcNode && funcNode->nodeType != snStatementBlock ) + { + if( funcNode->tokenType == ttIdentifier && file->TokenEquals(funcNode->tokenPos, funcNode->tokenLength, FINAL_TOKEN) ) + isFinal = true; + else if( funcNode->tokenType == ttIdentifier && file->TokenEquals(funcNode->tokenPos, funcNode->tokenLength, OVERRIDE_TOKEN) ) + isOverride = true; + + funcNode = funcNode->next; + } + + if( funcNode ) + funcNode->DisconnectParent(); + + if( funcNode == 0 && (objType == 0 || !objType->IsInterface()) ) + WriteError(TXT_PROPERTY_ACCESSOR_MUST_BE_IMPLEMENTED, file, node); + + // Setup the signature for the set accessor method + returnType = asCDataType::CreatePrimitive(ttVoid, false); + paramModifiers.PushLast(asTM_NONE); + paramNames.PushLast("value"); + paramTypes.PushLast(emulatedType); + defaultArgs.PushLast(0); + name = "set_" + emulatedName; + success = true; + } + else + WriteError(TXT_UNRECOGNIZED_VIRTUAL_PROPERTY_NODE, file, node); + + if( success ) + { + if( !isExistingShared ) + RegisterScriptFunction(funcNode, file, objType, isInterface, isGlobalFunction, ns, false, false, name, returnType, paramNames, paramTypes, paramModifiers, defaultArgs, isConst, false, false, isPrivate, isProtected, isOverride, isFinal, false); + else + { + // Free the funcNode as it won't be used + if( funcNode ) funcNode->Destroy(engine); + + // Should validate that the function really exists in the class/interface + bool found = false; + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[objType->methods[n]]; + if( func->name == name && + func->IsSignatureExceptNameEqual(returnType, paramTypes, paramModifiers, objType, isConst) ) + { + found = true; + break; + } + } + + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, objType->GetName()); + WriteError(str, file, node); + } + } + } + + node = next; + }; + + mainNode->Destroy(engine); + + return 0; +} + +int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) +{ + asCString name; + asCDataType returnType; + asCArray parameterNames; + asCArray parameterTypes; + asCArray inOutFlags; + asCArray defaultArgs; + bool isConstMethod, isOverride, isFinal, isConstructor, isDestructor, isPrivate, isProtected, isShared; + + if( ns == 0 ) + ns = engine->nameSpaces[0]; + + GetParsedFunctionDetails(node->firstChild, file, 0, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, ns); + CheckNameConflict(name.AddressOf(), node, file, ns); + + // Check that the same function hasn't been registered already in the namespace + asCArray funcs; + GetFunctionDescriptions(name.AddressOf(), funcs, ns); + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + if( func->IsSignatureExceptNameAndReturnTypeEqual(parameterTypes, inOutFlags, 0, false) ) + { + WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); + break; + } + } + + // Read the module name as well + asCScriptNode *nd = node->lastChild; + asASSERT( nd->nodeType == snConstant && nd->tokenType == ttStringConstant ); + asCString moduleName; + moduleName.Assign(&file->code[nd->tokenPos+1], nd->tokenLength-2); + + node->Destroy(engine); + + // Register the function + module->AddImportedFunction(importID, name, returnType, parameterTypes, inOutFlags, defaultArgs, ns, moduleName); + + return 0; +} + +asCScriptFunction *asCBuilder::GetFunctionDescription(int id) +{ + // TODO: import: This should be improved when the imported functions are removed + // Get the description from the engine + if( (id & FUNC_IMPORTED) == 0 ) + return engine->scriptFunctions[id]; + else + return engine->importedFunctions[id & ~FUNC_IMPORTED]->importedFunctionSignature; +} + +void asCBuilder::GetFunctionDescriptions(const char *name, asCArray &funcs, asSNameSpace *ns) +{ + asUINT n; + + // Get the script declared global functions + const asCArray &idxs = module->globalFunctions.GetIndexes(ns, name); + for( n = 0; n < idxs.GetLength(); n++ ) + { + const asCScriptFunction *f = module->globalFunctions.Get(idxs[n]); + asASSERT( f->objectType == 0 ); + funcs.PushLast(f->id); + } + + // Add the imported functions + // TODO: optimize: Linear search: This is probably not that critial. Also bindInformation will probably be removed in near future + for( n = 0; n < module->bindInformations.GetLength(); n++ ) + { + if( module->bindInformations[n]->importedFunctionSignature->name == name && + module->bindInformations[n]->importedFunctionSignature->nameSpace == ns ) + funcs.PushLast(module->bindInformations[n]->importedFunctionSignature->id); + } + + // Add the registered global functions + const asCArray &idxs2 = engine->registeredGlobalFuncs.GetIndexes(ns, name); + for( n = 0; n < idxs2.GetLength(); n++ ) + { + asCScriptFunction *f = engine->registeredGlobalFuncs.Get(idxs2[n]); + + // Verify if the module has access to the function + if( module->accessMask & f->accessMask ) + { + funcs.PushLast(f->id); + } + } +} + +// scope is only informed when looking for a base class' method +void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope, asCScriptNode *errNode, asCScriptCode *script) +{ + asASSERT(objectType); + + if( scope != "" ) + { + // If searching with a scope informed, then the node and script must also be informed for potential error reporting + asASSERT( errNode && script ); + + // If the scope contains ::identifier, then use the last identifier as the class name and the rest of it as the namespace + // TODO: child funcdef: A scope can include a template type, e.g. array + int n = scope.FindLast("::"); + asCString className = n >= 0 ? scope.SubString(n+2) : scope; + asCString nsName = n >= 0 ? scope.SubString(0, n) : ""; + + // If a namespace was specifically defined, then this must be used + asSNameSpace *ns = 0; + if (n >= 0) + { + if (nsName == "") + ns = engine->nameSpaces[0]; + else + ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script, 0, false); + + // If the namespace isn't found return silently and let the calling + // function report the error if it cannot resolve the symbol + if (ns == 0) + return; + } + + // Find the base class with the specified scope + while (objectType) + { + // If the name and namespace matches it is the correct class. If no + // specific namespace was given, then don't compare the namespace + if (objectType->name == className && (ns == 0 || objectType->nameSpace == ns)) + break; + + objectType = objectType->derivedFrom; + } + + // If the scope is not any of the base classes, then return no methods + if( objectType == 0 ) + return; + } + + // Find the methods in the object that match the name + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < objectType->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[objectType->methods[n]]; + if( func->name == name && + (!objIsConst || func->isReadOnly) && + (func->accessMask & module->accessMask) ) + { + // When the scope is defined the returned methods should be the true methods, not the virtual method stubs + if( scope == "" ) + methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id); + else + { + asCScriptFunction *virtFunc = engine->scriptFunctions[objectType->methods[n]]; + asCScriptFunction *realFunc = objectType->virtualFunctionTable[virtFunc->vfTableIdx]; + methods.PushLast(realFunc->id); + } + } + } +} +#endif + +void asCBuilder::WriteInfo(const asCString &scriptname, const asCString &message, int r, int c, bool pre) +{ + // Need to store the pre message in a structure + if( pre ) + { + engine->preMessage.isSet = true; + engine->preMessage.c = c; + engine->preMessage.r = r; + engine->preMessage.message = message; + engine->preMessage.scriptname = scriptname; + } + else + { + engine->preMessage.isSet = false; + + if( !silent ) + engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_INFORMATION, message.AddressOf()); + } +} + +void asCBuilder::WriteInfo(const asCString &message, asCScriptCode *file, asCScriptNode *node) +{ + int r = 0, c = 0; + if( node ) + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteInfo(file->name, message, r, c, false); +} + +void asCBuilder::WriteError(const asCString &message, asCScriptCode *file, asCScriptNode *node) +{ + int r = 0, c = 0; + if( node && file ) + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file ? file->name : asCString(""), message, r, c); +} + +void asCBuilder::WriteError(const asCString &scriptname, const asCString &message, int r, int c) +{ + numErrors++; + + if( !silent ) + engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_ERROR, message.AddressOf()); +} + +void asCBuilder::WriteWarning(const asCString &scriptname, const asCString &message, int r, int c) +{ + if( engine->ep.compilerWarnings ) + { + numWarnings++; + + if( !silent ) + engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_WARNING, message.AddressOf()); + } +} + +void asCBuilder::WriteWarning(const asCString &message, asCScriptCode *file, asCScriptNode *node) +{ + int r = 0, c = 0; + if( node && file ) + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteWarning(file ? file->name : asCString(""), message, r, c); +} + +// TODO: child funcdef: Should try to eliminate this function. GetNameSpaceFromNode is more complete +asCString asCBuilder::GetScopeFromNode(asCScriptNode *node, asCScriptCode *script, asCScriptNode **next) +{ + if (node->nodeType != snScope) + { + if (next) + *next = node; + return ""; + } + + asCString scope; + asCScriptNode *sn = node->firstChild; + if( sn->tokenType == ttScope ) + { + scope = "::"; + sn = sn->next; + } + + // TODO: child funcdef: A scope can have a template type as the innermost + while( sn && sn->next && sn->next->tokenType == ttScope ) + { + asCString tmp; + tmp.Assign(&script->code[sn->tokenPos], sn->tokenLength); + if( scope != "" && scope != "::" ) + scope += "::"; + scope += tmp; + sn = sn->next->next; + } + + if( next ) + *next = node->next; + + return scope; +} + +asSNameSpace *asCBuilder::GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next, asCObjectType **objType) +{ + if (objType) + *objType = 0; + + // If no scope has been informed, then return the implicit namespace + if (node->nodeType != snScope) + { + if (next) + *next = node; + return implicitNs ? implicitNs : engine->nameSpaces[0]; + } + + if (next) + *next = node->next; + + asCString scope; + asCScriptNode *sn = node->firstChild; + if (sn && sn->tokenType == ttScope) + { + scope = "::"; + sn = sn->next; + } + + while (sn) + { + if (sn->next->tokenType == ttScope) + { + asCString tmp; + tmp.Assign(&script->code[sn->tokenPos], sn->tokenLength); + if (scope != "" && scope != "::") + scope += "::"; + scope += tmp; + sn = sn->next->next; + } + else + { + // This is a template type + asASSERT(sn->next->nodeType == snDataType); + + asSNameSpace *ns = implicitNs; + if (scope != "") + ns = engine->FindNameSpace(scope.AddressOf()); + + asCString templateName(&script->code[sn->tokenPos], sn->tokenLength); + asCObjectType *templateType = GetObjectType(templateName.AddressOf(), ns); + if (templateType == 0 || (templateType->flags & asOBJ_TEMPLATE) == 0) + { + // TODO: child funcdef: Report error + return ns; + } + + if (objType) + *objType = GetTemplateInstanceFromNode(sn, script, templateType, implicitNs, 0); + + // Return no namespace, since this is an object type + return 0; + } + } + + asCTypeInfo *ti = 0; + asSNameSpace *ns = GetNameSpaceByString(scope, implicitNs ? implicitNs : engine->nameSpaces[0], node, script, &ti); + if (ti && objType) + *objType = CastToObjectType(ti); + return ns; +} + +asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, asCTypeInfo **scopeType, bool isRequired) +{ + if( scopeType ) + *scopeType = 0; + + asSNameSpace *ns = implicitNs; + if( nsName == "::" ) + ns = engine->nameSpaces[0]; + else if( nsName != "" ) + { + ns = engine->FindNameSpace(nsName.AddressOf()); + if (ns == 0 && scopeType) + { + asCString typeName; + asCString searchNs; + + // Split the scope with at the inner most :: + int pos = nsName.FindLast("::"); + bool recursive = false; + if (pos >= 0) + { + // Fully qualified namespace + typeName = nsName.SubString(pos + 2); + searchNs = nsName.SubString(0, pos); + } + else + { + // Partially qualified, use the implicit namespace and then search recursively for the type + typeName = nsName; + searchNs = implicitNs->name; + recursive = true; + } + + asSNameSpace *nsTmp = searchNs == "::" ? engine->nameSpaces[0] : engine->FindNameSpace(searchNs.AddressOf()); + asCTypeInfo *ti = 0; + while( !ti && nsTmp ) + { + // Check if the typeName is an existing type in the namespace + ti = GetType(typeName.AddressOf(), nsTmp, 0); + if (ti) + { + // The informed scope is not a namespace, but it does match a type + *scopeType = ti; + return 0; + } + nsTmp = recursive ? engine->GetParentNameSpace(nsTmp) : 0; + } + } + + if (ns == 0 && isRequired) + { + asCString msg; + msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, nsName.AddressOf()); + WriteError(msg, script, errNode); + } + } + + return ns; +} + +asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, asSNameSpace *implicitNamespace, bool acceptHandleForScope, asCObjectType *currentType) +{ + asASSERT(node->nodeType == snDataType); + + asCDataType dt; + + asCScriptNode *n = node->firstChild; + + bool isConst = false; + bool isImplicitHandle = false; + if( n->tokenType == ttConst ) + { + isConst = true; + n = n->next; + } + + // Determine namespace (or parent type) to search for the data type in + asCObjectType *parentType = 0; + asSNameSpace *ns = GetNameSpaceFromNode(n, file, implicitNamespace, &n, &parentType); + if( ns == 0 && parentType == 0 ) + { + // The namespace and parent type doesn't exist. Return a dummy type instead. + dt = asCDataType::CreatePrimitive(ttInt, false); + return dt; + } + + if( n->tokenType == ttIdentifier ) + { + bool found = false; + + asCString str; + str.Assign(&file->code[n->tokenPos], n->tokenLength); + + // Recursively search parent namespaces for matching type + asSNameSpace *origNs = ns; + asCObjectType *origParentType = parentType; + while( (ns || parentType) && !found ) + { + asCTypeInfo *ti = 0; + + if (currentType) + { + // If this is for a template type, then we must first determine if the + // identifier matches any of the template subtypes + if (currentType->flags & asOBJ_TEMPLATE) + { + for (asUINT subtypeIndex = 0; subtypeIndex < currentType->templateSubTypes.GetLength(); subtypeIndex++) + { + asCTypeInfo *type = currentType->templateSubTypes[subtypeIndex].GetTypeInfo(); + if (type && str == type->name) + { + ti = type; + break; + } + } + } + + if (ti == 0) + { + // Check if the type is a child type of the current type + ti = GetFuncDef(str.AddressOf(), 0, currentType); + if (ti) + { + dt = asCDataType::CreateType(ti, false); + found = true; + } + } + } + + if( ti == 0 ) + ti = GetType(str.AddressOf(), ns, parentType); + if( ti == 0 && !module && currentType ) + ti = GetTypeFromTypesKnownByObject(str.AddressOf(), currentType); + + if( ti && !found ) + { + found = true; + + if( ti->flags & asOBJ_IMPLICIT_HANDLE ) + isImplicitHandle = true; + + // Make sure the module has access to the object type + if( !module || (module->accessMask & ti->accessMask) ) + { + if( asOBJ_TYPEDEF == (ti->flags & asOBJ_TYPEDEF) ) + { + // TODO: typedef: A typedef should be considered different from the original type (though with implicit conversions between the two) + // Create primitive data type based on object flags + dt = CastToTypedefType(ti)->aliasForType; + dt.MakeReadOnly(isConst); + } + else + { + if( ti->flags & asOBJ_TEMPLATE ) + { + ti = GetTemplateInstanceFromNode(n, file, CastToObjectType(ti), implicitNamespace, currentType, &n); + if (ti == 0) + { + // Return a dummy + return asCDataType::CreatePrimitive(ttInt, false); + } + } + else if( n && n->next && n->next->nodeType == snDataType ) + { + asCString msg; + msg.Format(TXT_TYPE_s_NOT_TEMPLATE, ti->name.AddressOf()); + WriteError(msg, file, n); + } + + // Create object data type + if( ti ) + dt = asCDataType::CreateType(ti, isConst); + else + dt = asCDataType::CreatePrimitive(ttInt, isConst); + } + } + else + { + asCString msg; + msg.Format(TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE, (const char *)str.AddressOf()); + WriteError(msg, file, n); + + dt.SetTokenType(ttInt); + } + } + + if( !found ) + { + // Try to find it in the parent namespace + if( ns ) + ns = engine->GetParentNameSpace(ns); + if (parentType) + parentType = 0; + } + } + + if( !found ) + { + asCString msg; + if( origNs && origNs->name == "" ) + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS, str.AddressOf()); + else if (origNs) + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s, str.AddressOf(), origNs->name.AddressOf()); + else + { + // TODO: child funcdef: Message should explain that the identifier is not a type of the parent type + asCDataType pt = asCDataType::CreateType(origParentType, false); + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s, str.AddressOf(), pt.Format(origParentType->nameSpace, false).AddressOf()); + } + WriteError(msg, file, n); + + dt = asCDataType::CreatePrimitive(ttInt, isConst); + return dt; + } + } + else if( n->tokenType == ttAuto ) + { + dt = asCDataType::CreateAuto(isConst); + } + else + { + // Create primitive data type + dt = asCDataType::CreatePrimitive(n->tokenType, isConst); + } + + // Determine array dimensions and object handles + n = n->next; + while( n && (n->tokenType == ttOpenBracket || n->tokenType == ttHandle) ) + { + if( n->tokenType == ttOpenBracket ) + { + // Make sure the sub type can be instantiated + if( !dt.CanBeInstantiated() ) + { + asCString str; + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(ns).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(ns).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(ns).AddressOf()); + + WriteError(str, file, n); + } + + // Make the type an array (or multidimensional array) + if( dt.MakeArray(engine, module) < 0 ) + { + WriteError(TXT_NO_DEFAULT_ARRAY_TYPE, file, n); + break; + } + } + else + { + // Make the type a handle + if( dt.IsObjectHandle() ) + { + WriteError(TXT_HANDLE_OF_HANDLE_IS_NOT_ALLOWED, file, n); + break; + } + else if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) + { + WriteError(TXT_OBJECT_HANDLE_NOT_SUPPORTED, file, n); + break; + } + } + n = n->next; + } + + if( isImplicitHandle ) + { + // Make the type a handle + if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) + WriteError(TXT_OBJECT_HANDLE_NOT_SUPPORTED, file, n); + } + + return dt; +} + +asCObjectType *asCBuilder::GetTemplateInstanceFromNode(asCScriptNode *node, asCScriptCode *file, asCObjectType *templateType, asSNameSpace *implicitNamespace, asCObjectType *currentType, asCScriptNode **next) +{ + // Check if the subtype is a type or the template's subtype + // if it is the template's subtype then this is the actual template type, + // orderwise it is a template instance. + // Only do this for application registered interface, as the + // scripts cannot implement templates. + asCArray subTypes; + asUINT subtypeIndex; + asCScriptNode *n = node; + while (n && n->next && n->next->nodeType == snDataType) + { + n = n->next; + + // When parsing function definitions for template registrations (currentType != 0) it is necessary + // to pass in the current template type to the recursive call since it is this ones sub-template types + // that should be allowed. + asCDataType subType = CreateDataTypeFromNode(n, file, implicitNamespace, false, module ? 0 : (currentType ? currentType : templateType)); + subTypes.PushLast(subType); + + if (subType.IsReadOnly()) + { + asCString msg; + msg.Format(TXT_TMPL_SUBTYPE_MUST_NOT_BE_READ_ONLY); + WriteError(msg, file, n); + + // Return a dummy + return 0; + } + } + + if (next) + *next = n; + + if (subTypes.GetLength() != templateType->templateSubTypes.GetLength()) + { + asCString msg; + msg.Format(TXT_TMPL_s_EXPECTS_d_SUBTYPES, templateType->name.AddressOf(), int(templateType->templateSubTypes.GetLength())); + WriteError(msg, file, node); + + // Return a dummy + return 0; + } + + // Check if any of the given subtypes are different from the template's declared subtypes + bool isDifferent = false; + for (subtypeIndex = 0; subtypeIndex < subTypes.GetLength(); subtypeIndex++) + { + if (subTypes[subtypeIndex].GetTypeInfo() != templateType->templateSubTypes[subtypeIndex].GetTypeInfo()) + { + isDifferent = true; + break; + } + } + + if (isDifferent) + { + // This is a template instance + // Need to find the correct object type + asCObjectType *otInstance = engine->GetTemplateInstanceType(templateType, subTypes, module); + + if (otInstance && otInstance->scriptSectionIdx < 0) + { + // If this is the first time the template instance is used, store where it was declared from + otInstance->scriptSectionIdx = engine->GetScriptSectionNameIndex(file->name.AddressOf()); + int row, column; + file->ConvertPosToRowCol(n->tokenPos, &row, &column); + otInstance->declaredAt = (row & 0xFFFFF) | (column << 20); + } + + if (!otInstance) + { + asCString sub = subTypes[0].Format(templateType->nameSpace); + for (asUINT s = 1; s < subTypes.GetLength(); s++) + { + sub += ","; + sub += subTypes[s].Format(templateType->nameSpace); + } + asCString msg; + msg.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, templateType->name.AddressOf(), sub.AddressOf()); + WriteError(msg, file, n); + } + + return otInstance; + } + + return templateType; +} + +asCDataType asCBuilder::ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlags, bool *autoHandle) +{ + asCDataType dt = type; + + if( inOutFlags ) *inOutFlags = asTM_NONE; + + // Is the argument sent by reference? + asCScriptNode *n = node->firstChild; + if( n && n->tokenType == ttAmp ) + { + if (dt.GetTokenType() == ttVoid) + { + asCString msg; + msg.Format(TXT_TYPE_s_CANNOT_BE_REFERENCE, type.Format(0).AddressOf()); + WriteError(msg, file, node->firstChild); + return dt; + } + + dt.MakeReference(true); + n = n->next; + + if( n ) + { + if( inOutFlags ) + { + if( n->tokenType == ttIn ) + *inOutFlags = asTM_INREF; + else if( n->tokenType == ttOut ) + *inOutFlags = asTM_OUTREF; + else if( n->tokenType == ttInOut ) + *inOutFlags = asTM_INOUTREF; + else + asASSERT(false); + } + + n = n->next; + } + else + { + if( inOutFlags ) + *inOutFlags = asTM_INOUTREF; // ttInOut + } + + if( !engine->ep.allowUnsafeReferences && + inOutFlags && *inOutFlags == asTM_INOUTREF ) + { + // Verify that the base type support &inout parameter types + if( !dt.IsObject() || dt.IsObjectHandle() || !((dt.GetTypeInfo()->flags & asOBJ_NOCOUNT) || (CastToObjectType(dt.GetTypeInfo())->beh.addref && CastToObjectType(dt.GetTypeInfo())->beh.release)) ) + WriteError(TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT, file, node->firstChild); + } + } + + if( autoHandle ) *autoHandle = false; + + if( n && n->tokenType == ttPlus ) + { + // Autohandles are not supported for types with NOCOUNT + // If the type is not a handle then there was an error with building the type, but + // this error would already have been reported so no need to report another error here + if( dt.IsObjectHandle() && (dt.GetTypeInfo()->flags & asOBJ_NOCOUNT) ) + WriteError(TXT_AUTOHANDLE_CANNOT_BE_USED_FOR_NOCOUNT, file, node->firstChild); + + if( autoHandle ) *autoHandle = true; + } + + if (n && n->tokenType == ttIdentifier) + { + asCString str; + str.Assign(&file->code[n->tokenPos], n->tokenLength); + if (str == IF_HANDLE_TOKEN) + dt.SetIfHandleThenConst(true); + else + { + // TODO: Should give error if not currently parsing template registration + asCString msg; + msg.Format(TXT_UNEXPECTED_TOKEN_s, str.AddressOf()); + WriteError(msg, file, node->firstChild); + } + } + + return dt; +} + +asCTypeInfo *asCBuilder::GetType(const char *type, asSNameSpace *ns, asCObjectType *parentType) +{ + asASSERT((ns == 0 && parentType) || (ns && parentType == 0)); + + if (ns) + { + asCTypeInfo *ti = engine->GetRegisteredType(type, ns); + if (!ti && module) + ti = module->GetType(type, ns); + return ti; + } + else + { + // Recursively check base classes + asCObjectType *currType = parentType; + while (currType) + { + for (asUINT n = 0; n < currType->childFuncDefs.GetLength(); n++) + { + asCFuncdefType *funcDef = currType->childFuncDefs[n]; + if (funcDef && funcDef->name == type) + return funcDef; + } + currType = currType->derivedFrom; + } + } + + return 0; +} + +asCObjectType *asCBuilder::GetObjectType(const char *type, asSNameSpace *ns) +{ + return CastToObjectType(GetType(type, ns, 0)); +} + +#ifndef AS_NO_COMPILER +// This function will return true if there are any types in the engine or module +// with the given name. The namespace is ignored in this verification. +bool asCBuilder::DoesTypeExist(const asCString &type) +{ + asUINT n; + + // This function is only used when parsing expressions for building bytecode + // and this is only done after all types are known. For this reason the types + // can be safely cached in a map for quick lookup. Once the builder is released + // the cache will also be destroyed thus avoiding unnecessary memory consumption. + if( !hasCachedKnownTypes ) + { + // Only do this once + hasCachedKnownTypes = true; + + // Add registered types + asSMapNode *cursor; + engine->allRegisteredTypes.MoveFirst(&cursor); + while( cursor ) + { + if( !knownTypes.MoveTo(0, cursor->key.name) ) + knownTypes.Insert(cursor->key.name, true); + + engine->allRegisteredTypes.MoveNext(&cursor, cursor); + } + + if (module) + { + // Add script classes and interfaces + for (n = 0; n < module->classTypes.GetLength(); n++) + if (!knownTypes.MoveTo(0, module->classTypes[n]->name)) + knownTypes.Insert(module->classTypes[n]->name, true); + + // Add script enums + for (n = 0; n < module->enumTypes.GetLength(); n++) + if (!knownTypes.MoveTo(0, module->enumTypes[n]->name)) + knownTypes.Insert(module->enumTypes[n]->name, true); + + // Add script typedefs + for (n = 0; n < module->typeDefs.GetLength(); n++) + if (!knownTypes.MoveTo(0, module->typeDefs[n]->name)) + knownTypes.Insert(module->typeDefs[n]->name, true); + + // Add script funcdefs + for (n = 0; n < module->funcDefs.GetLength(); n++) + if (!knownTypes.MoveTo(0, module->funcDefs[n]->name)) + knownTypes.Insert(module->funcDefs[n]->name, true); + } + } + + // Check if the type is known + return knownTypes.MoveTo(0, type); +} +#endif + +asCTypeInfo *asCBuilder::GetTypeFromTypesKnownByObject(const char *type, asCObjectType *currentType) +{ + if (currentType->name == type) + return currentType; + + asUINT n; + + asCTypeInfo *found = 0; + + for (n = 0; found == 0 && n < currentType->properties.GetLength(); n++) + if (currentType->properties[n]->type.GetTypeInfo() && + currentType->properties[n]->type.GetTypeInfo()->name == type) + found = currentType->properties[n]->type.GetTypeInfo(); + + for (n = 0; found == 0 && n < currentType->methods.GetLength(); n++) + { + asCScriptFunction *func = engine->scriptFunctions[currentType->methods[n]]; + if (func->returnType.GetTypeInfo() && + func->returnType.GetTypeInfo()->name == type) + found = func->returnType.GetTypeInfo(); + + for (asUINT f = 0; found == 0 && f < func->parameterTypes.GetLength(); f++) + if (func->parameterTypes[f].GetTypeInfo() && + func->parameterTypes[f].GetTypeInfo()->name == type) + found = func->parameterTypes[f].GetTypeInfo(); + } + + if (found) + { + // In case we find a template instance it mustn't be returned + // because it is not known if the subtype is really matching + if (found->flags & asOBJ_TEMPLATE) + return 0; + } + + return found; +} + +asCFuncdefType *asCBuilder::GetFuncDef(const char *type, asSNameSpace *ns, asCObjectType *parentType) +{ + asASSERT((ns == 0 && parentType) || (ns && parentType == 0)); + + if (ns) + { + for (asUINT n = 0; n < engine->registeredFuncDefs.GetLength(); n++) + { + asCFuncdefType *funcDef = engine->registeredFuncDefs[n]; + // TODO: access: Only return the definitions that the module has access to + if (funcDef && funcDef->nameSpace == ns && funcDef->name == type) + return funcDef; + } + + if (module) + { + for (asUINT n = 0; n < module->funcDefs.GetLength(); n++) + { + asCFuncdefType *funcDef = module->funcDefs[n]; + if (funcDef && funcDef->nameSpace == ns && funcDef->name == type) + return funcDef; + } + } + } + else + { + // Recursively check base classes + asCObjectType *currType = parentType; + while (currType) + { + for (asUINT n = 0; n < currType->childFuncDefs.GetLength(); n++) + { + asCFuncdefType *funcDef = currType->childFuncDefs[n]; + if (funcDef && funcDef->name == type) + return funcDef; + } + currType = currType->derivedFrom; + } + } + + return 0; +} + +#ifndef AS_NO_COMPILER + +int asCBuilder::GetEnumValueFromType(asCEnumType *type, const char *name, asCDataType &outDt, asDWORD &outValue) +{ + if( !type || !(type->flags & asOBJ_ENUM) ) + return 0; + + for( asUINT n = 0; n < type->enumValues.GetLength(); ++n ) + { + if( type->enumValues[n]->name == name ) + { + outDt = asCDataType::CreateType(type, true); + outValue = type->enumValues[n]->value; + return 1; + } + } + + return 0; +} + +int asCBuilder::GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue, asSNameSpace *ns) +{ + bool found = false; + + // Search all available enum types + asUINT t; + for( t = 0; t < engine->registeredEnums.GetLength(); t++ ) + { + asCEnumType *et = engine->registeredEnums[t]; + if( ns != et->nameSpace ) continue; + + // Don't bother with types the module doesn't have access to + if( (et->accessMask & module->accessMask) == 0 ) + continue; + + if( GetEnumValueFromType(et, name, outDt, outValue) ) + { + if( !found ) + found = true; + else + { + // Found more than one value in different enum types + return 2; + } + } + } + + for( t = 0; t < module->enumTypes.GetLength(); t++ ) + { + asCEnumType *et = module->enumTypes[t]; + if( ns != et->nameSpace ) continue; + + if( GetEnumValueFromType(et, name, outDt, outValue) ) + { + if( !found ) + found = true; + else + { + // Found more than one value in different enum types + return 2; + } + } + } + + if( found ) + return 1; + + // Didn't find any value + return 0; +} + +#endif // AS_NO_COMPILER + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_bytecode.cpp b/3rdparty/angelscript/src/as_bytecode.cpp new file mode 100644 index 0000000..3482598 --- /dev/null +++ b/3rdparty/angelscript/src/as_bytecode.cpp @@ -0,0 +1,2929 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_bytecode.cpp +// +// A class for constructing the final byte code +// + +#include // fopen(), fprintf(), fclose() + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_bytecode.h" +#include "as_debug.h" // mkdir() +#include "as_array.h" +#include "as_string.h" +#include "as_scriptengine.h" +#include "as_debug.h" + +BEGIN_AS_NAMESPACE + +asCByteCode::asCByteCode(asCScriptEngine *engine) +{ + first = 0; + last = 0; + largestStackUsed = -1; + temporaryVariables = 0; + + this->engine = engine; +} + +asCByteCode::~asCByteCode() +{ + ClearAll(); +} + +void asCByteCode::Finalize(const asCArray &tempVariableOffsets) +{ + temporaryVariables = &tempVariableOffsets; + + // verify the bytecode + PostProcess(); + + // Optimize the code + Optimize(); + + // Resolve jumps + ResolveJumpAddresses(); + + // Build line numbers buffer + ExtractLineNumbers(); +} + +void asCByteCode::ClearAll() +{ + asCByteInstruction *del = first; + + while( del ) + { + first = del->next; + engine->memoryMgr.FreeByteInstruction(del); + del = first; + } + + first = 0; + last = 0; + + lineNumbers.SetLength(0); + + largestStackUsed = -1; +} + +void asCByteCode::InsertIfNotExists(asCArray &vars, int var) +{ + if( !vars.Exists(var) ) + vars.PushLast(var); +} + +void asCByteCode::GetVarsUsed(asCArray &vars) +{ + TimeIt("asCByteCode::GetVarsUsed"); + + asCByteInstruction *curr = first; + while( curr ) + { + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + InsertIfNotExists(vars, curr->wArg[1]); + InsertIfNotExists(vars, curr->wArg[2]); + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_W_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_DW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + InsertIfNotExists(vars, curr->wArg[1]); + } + else if( curr->op == asBC_LoadThisR ) + { + InsertIfNotExists(vars, 0); + } + + curr = curr->next; + } +} + +bool asCByteCode::IsVarUsed(int offset) +{ + TimeIt("asCByteCode::IsVarUsed"); + + asCByteInstruction *curr = first; + while( curr ) + { + // Verify all ops that use variables + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + if( curr->wArg[0] == offset || curr->wArg[1] == offset || curr->wArg[2] == offset ) + return true; + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_W_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_DW_ARG ) + { + if( curr->wArg[0] == offset ) + return true; + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG ) + { + if( curr->wArg[0] == offset || curr->wArg[1] == offset ) + return true; + } + else if( curr->op == asBC_LoadThisR ) + { + if( offset == 0 ) + return true; + } + + curr = curr->next; + } + + return false; +} + +void asCByteCode::ExchangeVar(int oldOffset, int newOffset) +{ + asASSERT(oldOffset != 0); + + asCByteInstruction *curr = first; + while( curr ) + { + // Verify all ops that use variables + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + if( curr->wArg[1] == oldOffset ) + curr->wArg[1] = (short)newOffset; + if( curr->wArg[2] == oldOffset ) + curr->wArg[2] = (short)newOffset; + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_W_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_DW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + if( curr->wArg[1] == oldOffset ) + curr->wArg[1] = (short)newOffset; + } + + curr = curr->next; + } +} + +void asCByteCode::AddPath(asCArray &paths, asCByteInstruction *instr, int stackSize) +{ + if( instr->marked ) + { + // Verify the size of the stack + asASSERT(instr->stackSize == stackSize); + } + else + { + // Add the destination to the code paths + instr->marked = true; + instr->stackSize = stackSize; + paths.PushLast(instr); + } +} + +asCByteInstruction *asCByteCode::ChangeFirstDeleteNext(asCByteInstruction *curr, asEBCInstr bc) +{ + curr->op = bc; + + if( curr->next ) DeleteInstruction(curr->next); + + // Continue optimization with the instruction before the altered one + if( curr->prev ) + return curr->prev; + else + return curr; +} + +asCByteInstruction *asCByteCode::DeleteFirstChangeNext(asCByteInstruction *curr, asEBCInstr bc) +{ + asASSERT( curr->next ); + + asCByteInstruction *instr = curr->next; + instr->op = bc; + + DeleteInstruction(curr); + + // Continue optimization with the instruction before the altered one + if( instr->prev ) + return instr->prev; + else + return instr; +} + +void asCByteCode::InsertBefore(asCByteInstruction *before, asCByteInstruction *instr) +{ + asASSERT(instr->next == 0); + asASSERT(instr->prev == 0); + + if( before->prev ) before->prev->next = instr; + instr->prev = before->prev; + before->prev = instr; + instr->next = before; + + if( first == before ) first = instr; +} + +void asCByteCode::RemoveInstruction(asCByteInstruction *instr) +{ + if( instr == first ) first = first->next; + if( instr == last ) last = last->prev; + + if( instr->prev ) instr->prev->next = instr->next; + if( instr->next ) instr->next->prev = instr->prev; + + instr->next = 0; + instr->prev = 0; +} + +bool asCByteCode::CanBeSwapped(asCByteInstruction *curr) +{ + asASSERT( curr->op == asBC_SwapPtr ); + + if( !curr->prev || !curr->prev->prev ) return false; + + asCByteInstruction *b = curr->prev; + asCByteInstruction *a = b->prev; + + if( a->op != asBC_PshNull && + a->op != asBC_PshVPtr && + a->op != asBC_PSF ) + return false; + + if( b->op != asBC_PshNull && + b->op != asBC_PshVPtr && + b->op != asBC_PSF ) + return false; + + return true; +} + +asCByteInstruction *asCByteCode::GoBack(asCByteInstruction *curr) +{ + // Go back 2 instructions + if( !curr ) return 0; + if( curr->prev ) curr = curr->prev; + if( curr->prev ) curr = curr->prev; + return curr; +} + +asCByteInstruction *asCByteCode::GoForward(asCByteInstruction *curr) +{ + // Go forward 2 instructions + if( !curr ) return 0; + if( curr->next ) curr = curr->next; + if( curr->next ) curr = curr->next; + return curr; +} + +bool asCByteCode::PostponeInitOfTemp(asCByteInstruction *curr, asCByteInstruction **next) +{ + TimeIt("asCByteCode::PostponeInitOfTemp"); + + // This is not done for pointers + if( (curr->op != asBC_SetV4 && curr->op != asBC_SetV8) || + !IsTemporary(curr->wArg[0]) ) return false; + + // Move the initialization to just before it's use. + // Don't move it beyond any labels or jumps. + asCByteInstruction *use = curr->next; + while( use ) + { + if( IsTempVarReadByInstr(use, curr->wArg[0]) ) + break; + + if( IsTempVarOverwrittenByInstr(use, curr->wArg[0]) ) + return false; + + if( IsInstrJmpOrLabel(use) ) + return false; + + use = use->next; + } + + if( use && use->prev != curr ) + { + asCByteInstruction *orig = curr->next; + + // Move the instruction + RemoveInstruction(curr); + InsertBefore(use, curr); + + // Try a RemoveUnusedValue to see if it can be combined with the other + if( RemoveUnusedValue(curr, 0) ) + { + // Optimizations should continue from the instruction that uses the value + *next = orig; + return true; + } + + // Return the instructions to its original position as it wasn't useful + RemoveInstruction(curr); + InsertBefore(orig, curr); + } + + return false; +} + +bool asCByteCode::RemoveUnusedValue(asCByteInstruction *curr, asCByteInstruction **next) +{ + TimeIt("asCByteCode::RemoveUnusedValue"); + + asCByteInstruction *dummy; + if( next == 0 ) + next = &dummy; + + // TODO: runtime optimize: Should work for 64bit types as well + + // TODO: runtime optimize: Need a asBCTYPE_rwW_ARG to cover the instructions that read + // and write to the same variable. Currently they are considered + // as readers only, so they are not optimized away. This includes + // NOT, BNOT, IncV, DecV, NEG, iTOf (and all other type casts) + + // The value isn't used for anything + if( curr->op != asBC_FREE && // Can't remove the FREE instruction + (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG) && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr, curr->wArg[0]) ) + { + if( curr->op == asBC_LdGRdR4 && IsTempRegUsed(curr) ) + { + curr->op = asBC_LDG; + *next = GoForward(curr); + return true; + } + + *next = GoForward(DeleteInstruction(curr)); + return true; + } + + if( curr->op == asBC_SetV4 && curr->next ) + { + // The value is immediately used and then never again + if( (curr->next->op == asBC_CMPi || + curr->next->op == asBC_CMPf || + curr->next->op == asBC_CMPu) && + curr->wArg[0] == curr->next->wArg[1] && + IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + if( curr->next->op == asBC_CMPi ) curr->next->op = asBC_CMPIi; + else if( curr->next->op == asBC_CMPf ) curr->next->op = asBC_CMPIf; + else if( curr->next->op == asBC_CMPu ) curr->next->op = asBC_CMPIu; + curr->next->size = asBCTypeSize[asBCInfo[asBC_CMPIi].type]; + curr->next->arg = curr->arg; + *next = GoForward(DeleteInstruction(curr)); + return true; + } + + // The value is immediately used and then never again + if( (curr->next->op == asBC_ADDi || + curr->next->op == asBC_SUBi || + curr->next->op == asBC_MULi || + curr->next->op == asBC_ADDf || + curr->next->op == asBC_SUBf || + curr->next->op == asBC_MULf) && + curr->wArg[0] == curr->next->wArg[2] && + (curr->next->wArg[0] == curr->wArg[0] || // The variable is overwritten + (IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0]))) ) + { + if( curr->next->op == asBC_ADDi ) curr->next->op = asBC_ADDIi; + else if( curr->next->op == asBC_SUBi ) curr->next->op = asBC_SUBIi; + else if( curr->next->op == asBC_MULi ) curr->next->op = asBC_MULIi; + else if( curr->next->op == asBC_ADDf ) curr->next->op = asBC_ADDIf; + else if( curr->next->op == asBC_SUBf ) curr->next->op = asBC_SUBIf; + else if( curr->next->op == asBC_MULf ) curr->next->op = asBC_MULIf; + curr->next->size = asBCTypeSize[asBCInfo[asBC_ADDIi].type]; + curr->next->arg = curr->arg; + *next = GoForward(DeleteInstruction(curr)); + return true; + } + + if( (curr->next->op == asBC_ADDi || + curr->next->op == asBC_MULi || + curr->next->op == asBC_ADDf || + curr->next->op == asBC_MULf) && + curr->wArg[0] == curr->next->wArg[1] && + (curr->next->wArg[0] == curr->wArg[0] || // The variable is overwritten + (IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0]))) ) + { + if( curr->next->op == asBC_ADDi ) curr->next->op = asBC_ADDIi; + else if( curr->next->op == asBC_MULi ) curr->next->op = asBC_MULIi; + else if( curr->next->op == asBC_ADDf ) curr->next->op = asBC_ADDIf; + else if( curr->next->op == asBC_MULf ) curr->next->op = asBC_MULIf; + curr->next->size = asBCTypeSize[asBCInfo[asBC_ADDIi].type]; + curr->next->arg = curr->arg; + + // The order of the operands are changed + curr->next->wArg[1] = curr->next->wArg[2]; + + *next = GoForward(DeleteInstruction(curr)); + return true; + } + + // The constant value is immediately moved to another variable and then not used again + if( curr->next->op == asBC_CpyVtoV4 && + curr->wArg[0] == curr->next->wArg[1] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->wArg[0] = curr->next->wArg[0]; + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + + // The constant is copied to a temp and then immediately pushed on the stack + if( curr->next->op == asBC_PshV4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_PshC4; + curr->stackInc = asBCInfo[asBC_PshC4].stackInc; + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + + // The constant is copied to a global variable and then never used again + if( curr->next->op == asBC_CpyVtoG4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_SetG4; + curr->size = asBCTypeSize[asBCInfo[asBC_SetG4].type]; + *(((asDWORD*)&curr->arg)+AS_PTR_SIZE) = *ARG_DW(curr->arg); + *ARG_PTR(curr->arg) = *ARG_PTR(curr->next->arg); + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + } + + // The value is immediately moved to another variable and then not used again + if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG) && + curr->next && curr->next->op == asBC_CpyVtoV4 && + curr->wArg[0] == curr->next->wArg[1] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->wArg[0] = curr->next->wArg[0]; + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + + // The register is copied to a temp variable and then back to the register again without being used afterwards + if( curr->op == asBC_CpyRtoV4 && curr->next && curr->next->op == asBC_CpyVtoR4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + // Delete both instructions + DeleteInstruction(curr->next); + *next = GoForward(DeleteInstruction(curr)); + return true; + } + + // The global value is copied to a temp and then immediately pushed on the stack + if( curr->op == asBC_CpyGtoV4 && curr->next && curr->next->op == asBC_PshV4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_PshG4; + curr->size = asBCTypeSize[asBCInfo[asBC_PshG4].type]; + curr->stackInc = asBCInfo[asBC_PshG4].stackInc; + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + + // The constant is assigned to a variable, then the value of the variable + // pushed on the stack, and then the variable is never used again + if( curr->op == asBC_SetV8 && curr->next && curr->next->op == asBC_PshV8 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_PshC8; + curr->stackInc = asBCInfo[asBC_PshC8].stackInc; + *next = GoForward(DeleteInstruction(curr->next)); + return true; + } + + return false; +} + +bool asCByteCode::IsTemporary(int offset) +{ + TimeIt("asCByteCode::IsTemporary"); + + asASSERT(temporaryVariables); + + return temporaryVariables->Exists(offset); +} + +void asCByteCode::OptimizeLocally(const asCArray &tempVariableOffsets) +{ + // This function performs the optimizations that doesn't require global knowledge of the + // entire function, e.g. replacement of sequences of bytecodes for specialized instructions. + + if( !engine->ep.optimizeByteCode ) + return; + + temporaryVariables = &tempVariableOffsets; + + // TODO: runtime optimize: VAR + GET... should be optimized if the only instructions between them are trivial, i.e. no + // function calls that can suspend the execution. + + // TODO: runtime optimize: Remove temporary copies of handles, when the temp is just copied to yet another location + + // TODO: runtime optimize: A single bytecode for incrementing a variable, comparing, and jumping can probably improve + // loops a lot. How often do these loops really occur? + + // TODO: runtime optimize: Need a bytecode BC_AddRef so that BC_CALLSYS doesn't have to be used for this trivial call + + // TODO: optimize: Should possibly do two loops. Some of the checks are best doing by iterating from + // the end to beginning, e.g. the removal of unused values. Other checks are best + // doing by iterating from the beginning to end, e.g. replacement of sequences with + // shorter ones. By doing this, we should be able to avoid backtracking with every + // change thus avoid unnecessary duplicate checks. + + // Iterate through the bytecode instructions in the reverse order. + // An optimization in an instruction may mean that another instruction before that + // can also be optimized, e.g. if an add instruction is removed because the result is not + // used, then the instructions that created the operands may potentially also be removed. + asCByteInstruction *instr = last; + while( instr ) + { + asCByteInstruction *curr = instr; + instr = instr->prev; + + // Remove instructions when the result is not used anywhere + // This will return true if the instruction is deleted, and + // false if it is not deleted. Observe that the instruction + // can be modified. + if( RemoveUnusedValue(curr, &instr) ) continue; + + // Postpone initializations so that they may be combined in the second pass. + // If the initialization is postponed, then the optimizations should continue + // from where the value was used, so instr will be updated to point to that. + if( PostponeInitOfTemp(curr, &instr) ) continue; + + // Look for sequences that can be replaced with shorter ones + const asEBCInstr currOp = curr->op; + if( currOp == asBC_SwapPtr ) + { + // XXX x, YYY y, SwapPtr -> YYY y, XXX x + if( CanBeSwapped(curr) ) + { + // Delete the SwapPtr + DeleteInstruction(curr); + + // Swap instructions + asCByteInstruction *a = instr->prev; + RemoveInstruction(instr); + InsertBefore(a, instr); + + // Continue the optimization from the second instruction + instr = GoForward(a); + continue; + } + } + else if( currOp == asBC_ClrHi ) + { + // T??, ClrHi -> T?? + if( instr && + (instr->op == asBC_TZ || + instr->op == asBC_TNZ || + instr->op == asBC_TS || + instr->op == asBC_TNS || + instr->op == asBC_TP || + instr->op == asBC_TNP) ) + { + // Remove the ClrHi instruction since the test + // instructions always clear the top bytes anyway + instr = GoForward(DeleteInstruction(curr)); + continue; + } + + // ClrHi, JZ -> JLowZ + if( curr->next && + curr->next->op == asBC_JZ ) + { + curr->next->op = asBC_JLowZ; + instr = GoForward(DeleteInstruction(curr)); + continue; + } + + // ClrHi, JNZ -> JLowNZ + if( curr->next && + curr->next->op == asBC_JNZ ) + { + curr->next->op = asBC_JLowNZ; + instr = GoForward(DeleteInstruction(curr)); + continue; + } + } + else if( currOp == asBC_LDV && curr->next ) + { + // LDV x, INCi -> IncVi x + if( curr->next->op == asBC_INCi && !IsTempRegUsed(curr->next) ) + { + curr->op = asBC_IncVi; + DeleteInstruction(curr->next); + instr = GoForward(curr); + } + // LDV x, DECi -> DecVi x + else if( curr->next->op == asBC_DECi && !IsTempRegUsed(curr->next) ) + { + curr->op = asBC_DecVi; + DeleteInstruction(curr->next); + instr = GoForward(curr); + } + } + else if( currOp == asBC_LDG && curr->next ) + { + // LDG x, WRTV4 y -> CpyVtoG4 y, x + if( curr->next->op == asBC_WRTV4 && !IsTempRegUsed(curr->next) ) + { + curr->op = asBC_CpyVtoG4; + curr->size = asBCTypeSize[asBCInfo[asBC_CpyVtoG4].type]; + curr->wArg[0] = curr->next->wArg[0]; + DeleteInstruction(curr->next); + instr = GoForward(curr); + } + // LDG x, RDR4 y -> CpyGtoV4 y, x + else if( curr->next->op == asBC_RDR4 ) + { + if( !IsTempRegUsed(curr->next) ) + curr->op = asBC_CpyGtoV4; + else + curr->op = asBC_LdGRdR4; + curr->size = asBCTypeSize[asBCInfo[asBC_CpyGtoV4].type]; + curr->wArg[0] = curr->next->wArg[0]; + DeleteInstruction(curr->next); + instr = GoForward(curr); + } + } + else if( currOp == asBC_CHKREF ) + { + // CHKREF, ADDSi -> ADDSi + // CHKREF, RDSPtr -> RDSPtr + if( curr->next && + (curr->next->op == asBC_ADDSi || curr->next->op == asBC_RDSPtr) ) + { + // As ADDSi & RDSPtr already checks the pointer the CHKREF instruction is unnecessary + instr = GoForward(DeleteInstruction(curr)); + } + // ADDSi, CHKREF -> ADDSi + // PGA, CHKREF -> PGA + // PSF, CHKREF -> PSF + else if( instr && + (instr->op == asBC_ADDSi || + instr->op == asBC_PGA || + instr->op == asBC_PSF) ) + { + // ADDSi is guaranteed to work on valid pointers so CHKREF is not necessary. + // PGA and PSF always pushes a valid address on the stack. + instr = GoForward(DeleteInstruction(curr)); + } + // PGA, ChkRefS, CHKREF -> PGA, ChkRefS + else if( instr && instr->op == asBC_ChkRefS && + instr->prev && instr->prev->op == asBC_PGA ) + { + // Delete CHKREF since PGA always pushes a valid address on the stack + instr = GoForward(DeleteInstruction(curr)); + } + } + else if( currOp == asBC_PopPtr ) + { + // RDSPtr, PopPtr -> PopPtr + if( instr && instr->op == asBC_RDSPtr ) + { + instr = GoForward(DeleteInstruction(instr)); + } + // PshNull, RefCpyV, PopPtr -> FREE + else if( instr && instr->op == asBC_RefCpyV && + instr->prev && instr->prev->op == asBC_PshNull ) + { + DeleteInstruction(curr); + DeleteInstruction(instr->prev); + instr->op = asBC_FREE; + instr = GoForward(instr); + } + // PshVPtr y, PopPtr -> nothing + // PSF y , PopPtr -> nothing + // VAR y , PopPtr -> nothing + // PshNull , PopPtr -> nothing + // PshRPtr , PopPtr -> nothing + else if( instr && + (instr->op == asBC_PshRPtr || + instr->op == asBC_PSF || + instr->op == asBC_VAR || + instr->op == asBC_PshVPtr || + instr->op == asBC_PshNull) ) + { + // A pointer is pushed on the stack then immediately removed + // Remove both instructions as they cancel each other + DeleteInstruction(curr); + instr = GoForward(DeleteInstruction(instr)); + } + // PSF, ChkRefS, PopPtr -> ChkNullV + else if( instr && instr->op == asBC_ChkRefS && + instr->prev && instr->prev->op == asBC_PSF ) + { + instr = instr->prev; + instr->op = asBC_ChkNullV; + instr->stackInc = 0; + // Delete the PopPtr instruction + DeleteInstruction(curr); + // Delete the ChkRefS instruction + DeleteInstruction(instr->next); + instr = GoForward(instr); + } + // PshVPtr, CHKREF, PopPtr -> ChkNullV + else if( instr && instr->op == asBC_CHKREF && + instr->prev && instr->prev->op == asBC_PshVPtr ) + { + instr = instr->prev; + instr->op = asBC_ChkNullV; + instr->stackInc = 0; + DeleteInstruction(curr->prev); + DeleteInstruction(curr); + instr = GoForward(instr); + } + // STOREOBJ y, PSF y, RDSPtr, PSF x, REFCPY, FREE y, PopPtr -> FREE x, STOREOBJ x + else if( instr && instr->op == asBC_FREE ) + { + asCByteInstruction *i = instr->prev; + if( !i || i->op != asBC_REFCPY ) continue; + i = i->prev; + if( !i || i->op != asBC_PSF ) continue; + short x = i->wArg[0]; + i = i->prev; + if( !i || i->op != asBC_RDSPtr ) continue; + i = i->prev; + if( !i || i->op != asBC_PSF ) continue; + short y = i->wArg[0]; + i = i->prev; + if( !i || i->op != asBC_STOREOBJ || i->wArg[0] != y ) continue; + + // Don't do the substitution if the var y is not a temporary, or if it is used after PopPtr + if( !IsTemporary(y) || IsTempVarRead(curr, y) ) continue; + + // Transform the PopPtr into STOREOBJ + curr->op = asBC_STOREOBJ; + curr->stackInc = 0; + curr->wArg[0] = x; + curr->size = i->size; + + // Change arg of the FREE to x + // TODO: runtime optimize: The FREE instruction shouldn't be necessary. STOREOBJ should free the previous value by itself + instr->wArg[0] = x; + + // Delete all other instructions + DeleteInstruction(instr->prev); // REFCPY + DeleteInstruction(instr->prev); // PSF + DeleteInstruction(instr->prev); // RDSTR + DeleteInstruction(instr->prev); // PSF + DeleteInstruction(instr->prev); // STOREOBJ + + instr = GoForward(curr); + } + } + else if( currOp == asBC_RDSPtr ) + { + // PGA, RDSPtr -> PshGPtr + if( instr && instr->op == asBC_PGA ) + { + instr->op = asBC_PshGPtr; + DeleteInstruction(curr); + instr = GoForward(instr); + } + // ChkRefS, RDSPtr -> RDSPtr, CHKREF + else if( instr && instr->op == asBC_ChkRefS ) + { + // This exchange removes one pointer dereference, and also + // makes it easier to completely remove the CHKREF instruction + curr->op = asBC_CHKREF; + instr->op = asBC_RDSPtr; + instr = GoForward(curr); + } + // PSF, RDSPtr -> PshVPtr + else if( instr && instr->op == asBC_PSF ) + { + instr->op = asBC_PshVPtr; + instr = GoForward(DeleteInstruction(curr)); + } + // PSF, ChkRefS, RDSPtr -> PshVPtr, CHKREF + else if( instr && instr->op == asBC_ChkRefS && + instr->prev && instr->prev->op == asBC_PSF ) + { + instr->prev->op = asBC_PshVPtr; + instr->op = asBC_CHKREF; + instr = GoForward(DeleteInstruction(curr)); + } + } + else if( currOp == asBC_PopRPtr ) + { + // PshVPtr 0, ADDSi, PopRPtr -> LoadThisR + if( instr && instr->op == asBC_ADDSi && + instr->prev && instr->prev->op == asBC_PshVPtr && + instr->prev->wArg[0] == 0 ) + { + DeleteInstruction(instr->prev); + ChangeFirstDeleteNext(instr, asBC_LoadThisR); + instr = GoForward(instr); + } + // TODO: runtime optimize: PshVPtr x, PopRPtr -> LoadRObjR x, 0 + // PshVPtr x, ADDSi, PopRPtr -> LoadRObjR + else if( instr && instr->op == asBC_ADDSi && + instr->prev && instr->prev->op == asBC_PshVPtr && + instr->prev->wArg[0] != 0 ) + { + instr = instr->prev; + instr->op = asBC_LoadRObjR; + instr->size = asBCTypeSize[asBCInfo[asBC_LoadRObjR].type]; + instr->stackInc = asBCInfo[asBC_LoadRObjR].stackInc; + instr->wArg[1] = instr->next->wArg[0]; + *(asDWORD*)&instr->arg = *(asDWORD*)&instr->next->arg; + DeleteInstruction(instr->next); + DeleteInstruction(curr); + instr = GoForward(instr); + } + // PSF x, ADDSi, PopRPtr -> LoadVObjR + else if( instr && instr->op == asBC_ADDSi && + instr->prev && instr->prev->op == asBC_PSF ) + { + instr = instr->prev; + instr->op = asBC_LoadVObjR; + instr->size = asBCTypeSize[asBCInfo[asBC_LoadVObjR].type]; + instr->stackInc = asBCInfo[asBC_LoadVObjR].stackInc; + instr->wArg[1] = instr->next->wArg[0]; + *(asDWORD*)&instr->arg = *(asDWORD*)&instr->next->arg; + DeleteInstruction(instr->next); + DeleteInstruction(curr); + instr = GoForward(instr); + } + } + else if( currOp == asBC_REFCPY ) + { + // PSF x, REFCPY -> RefCpyV x + if( instr && instr->op == asBC_PSF ) + { + curr->op = asBC_RefCpyV; + curr->wArg[0] = instr->wArg[0]; + curr->stackInc = asBCInfo[asBC_LoadVObjR].stackInc; + DeleteInstruction(instr); + instr = GoForward(curr); + } + } + else if( ((currOp >= asBC_JZ && currOp <= asBC_JNP) || currOp == asBC_JLowZ || currOp == asBC_JLowNZ) && instr ) + { + // T**; J** +x -> J** +x + if( (instr->op == asBC_TZ && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TNZ && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JNZ)); + else if( (instr->op == asBC_TNZ && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TZ && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JZ)); + else if( (instr->op == asBC_TS && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TNS && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JNS)); + else if( (instr->op == asBC_TNS && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TS && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JS)); + else if( (instr->op == asBC_TP && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TNP && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JNP)); + else if( (instr->op == asBC_TNP && (currOp == asBC_JZ || currOp == asBC_JLowZ)) || + (instr->op == asBC_TP && (currOp == asBC_JNZ || currOp == asBC_JLowNZ)) ) + instr = GoForward(DeleteFirstChangeNext(instr, asBC_JP)); + } + else if( currOp == asBC_FREE && instr ) + { + // PSF, FREE -> FREE, PSF + if( instr->op == asBC_PSF ) + { + // This pattern usually happens when a function returns an object, or handle + // and then releases a temporary variable, possibly used in one of the arguments. + // By swapping the order of these instructions, the code can be further optimized + // to combine the PSF with the following instructions + RemoveInstruction(curr); + InsertBefore(instr, curr); + instr = GoForward(instr); + } + // VAR, FREE -> FREE, VAR + else if( instr->op == asBC_VAR ) + { + // Swap the two instructions, so that the VAR instruction + // gets closer to its corresponding GET instruction and thus + // has a greater chance of getting optimized + RemoveInstruction(curr); + InsertBefore(instr, curr); + instr = GoForward(instr); + } + } + else if( currOp == asBC_VAR ) + { + // VAR, PSF, GETOBJREF {PTR_SIZE} -> PshVPtr, PSF + if( curr->next && curr->next->op == asBC_PSF && + curr->next->next && curr->next->next->op == asBC_GETOBJREF && + curr->next->next->wArg[0] == AS_PTR_SIZE ) + { + curr->op = asBC_PshVPtr; + DeleteInstruction(curr->next->next); + instr = GoForward(curr); + } + // VAR a, GETREF 0 -> PSF a + else if( curr->next && curr->next->op == asBC_GETREF && curr->next->wArg[0] == 0 ) + { + ChangeFirstDeleteNext(curr, asBC_PSF); + instr = GoForward(curr); + } + // VAR a, GETOBJREF 0 -> PshVPtr a + else if( curr->next && curr->next->op == asBC_GETOBJREF && curr->next->wArg[0] == 0 ) + { + ChangeFirstDeleteNext(curr, asBC_PshVPtr); + instr = GoForward(curr); + } + // VAR, PSF, GETREF {PTR_SIZE} -> PSF, PSF + if( curr->next && curr->next->op == asBC_PSF && + curr->next->next && curr->next->next->op == asBC_GETREF && + curr->next->next->wArg[0] == AS_PTR_SIZE ) + { + curr->op = asBC_PSF; + DeleteInstruction(curr->next->next); + instr = GoForward(curr); + } + } + } + + // Optimize unnecessary refcpy for return handle. This scenario only happens for return statements + // and LOADOBJ can only be the last instruction before the RET, so doing this check after the rest of + // the optimizations have taken place saves us time. + if( last && last->op == asBC_LOADOBJ && IsTemporary(last->wArg[0]) ) + { + // A temporary handle is being loaded into the object register. + // Let's look for a trivial RefCpyV to that temporary variable, and a Free of the original + // variable. If this is found, then we can simply load the original value into the register + // and avoid both the RefCpy and the Free. + short tempVar = last->wArg[0]; + asCArray freedVars; + + instr = last->prev; + asASSERT( instr && instr->op == asBC_Block ); + instr = instr->prev; + while( instr && instr->op == asBC_FREE ) + { + freedVars.PushLast(instr->wArg[0]); + instr = instr->prev; + } + + // If there is any non-trivial cleanups, e.g. call to destructors, then we skip this optimizations + // TODO: runtime optimize: Do we need to skip it? Is there really a chance the local variable + // will be invalidated while the destructor, or any other function for + // that matter, is being called? + if( instr && instr->op == asBC_Block ) + { + // We expect a sequence PshVPtr, RefCpyV, PopPtr just before the clean up block + instr = instr->prev; + if( instr && instr->op == asBC_PopPtr ) instr = instr->prev; + if( instr && instr->op == asBC_RefCpyV && instr->wArg[0] == tempVar ) instr = instr->prev; + if( instr && instr->op == asBC_PshVPtr && freedVars.Exists(instr->wArg[0]) ) + { + // Update the LOADOBJ to load the local variable directly + tempVar = instr->wArg[0]; + last->wArg[0] = tempVar; + + // Remove the copy of the local variable into the temp + DeleteInstruction(instr->next); // deletes RefCpyV + DeleteInstruction(instr->next); // deletes PopPtr + DeleteInstruction(instr); // deletes PshVPtr + + // Find and remove the FREE instruction for the local variable too + instr = last->prev->prev; + while( instr ) + { + asASSERT( instr->op == asBC_FREE ); + if( instr->wArg[0] == tempVar ) + { + DeleteInstruction(instr); + break; + } + instr = instr->prev; + } + } + } + } +} + +void asCByteCode::Optimize() +{ + // This function performs the optimizations that require global knowledge of the entire function + + TimeIt("asCByteCode::Optimize"); + + if( !engine->ep.optimizeByteCode ) + return; + + // TODO: runtime optimize: The optimizer should be able to inline function calls. + // If the called function has only a few instructions, the function call should be inlined. + // This is especially useful with the factory stubs used for template types and script classes. + + asCByteInstruction *instr = first; + while( instr ) + { + asCByteInstruction *curr = instr; + instr = instr->next; + + const asEBCInstr currOp = curr->op; + + // Delete JitEntry if the JIT instructions are not supposed to be included + if( currOp == asBC_JitEntry && !engine->ep.includeJitInstructions ) + { + instr = GoBack(DeleteInstruction(curr)); + continue; + } + + if( instr ) + { + const asEBCInstr instrOp = instr->op; + + // PopPtr, RET b -> RET b + if( currOp == asBC_PopPtr && instrOp == asBC_RET ) + { + // We don't combine the PopPtr+RET because RET first restores + // the previous stack pointer and then pops the arguments + + // Delete PopPtr + instr = GoBack(DeleteInstruction(curr)); + } + else if( currOp == asBC_SUSPEND ) + { + // SUSPEND, JitEntry, SUSPEND -> SUSPEND + if( instrOp == asBC_JitEntry && instr->next && instr->next->op == asBC_SUSPEND ) + { + // Delete the two first instructions + DeleteInstruction(instr); + instr = GoBack(DeleteInstruction(curr)); + } + // SUSPEND, SUSPEND -> SUSPEND + else if( instrOp == asBC_SUSPEND ) + { + // Delete the first instruction + instr = GoBack(DeleteInstruction(curr)); + } + // SUSPEND, Block, SUSPEND -> Block, SUSPEND + else if( instrOp == asBC_Block && instr->next && instr->next->op == asBC_SUSPEND ) + { + // Delete the first instruction + instr = GoBack(DeleteInstruction(curr)); + } + } + else if( currOp == asBC_LINE ) + { + // LINE, JitEntry, LINE -> LINE + if( instrOp == asBC_JitEntry && instr->next && instr->next->op == asBC_LINE ) + { + // Delete the two first instructions + DeleteInstruction(instr); + instr = GoBack(DeleteInstruction(curr)); + } + // LINE, LINE -> LINE + else if( instrOp == asBC_LINE ) + { + // Delete the first instruction + instr = GoBack(DeleteInstruction(curr)); + } + // LINE, Block, LINE -> Block, LINE + else if( instrOp == asBC_Block && instr->next && instr->next->op == asBC_LINE ) + { + // Delete the first instruction + instr = GoBack(DeleteInstruction(curr)); + } + } + // JMP +0 -> remove + else if( currOp == asBC_JMP && instrOp == asBC_LABEL && *(int*)&curr->arg == instr->wArg[0] ) + instr = GoBack(DeleteInstruction(curr)); + } + } +} + +bool asCByteCode::IsTempVarReadByInstr(asCByteInstruction *curr, int offset) +{ + // Which instructions read from variables? + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG && + (int(curr->wArg[1]) == offset || int(curr->wArg[2]) == offset) ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_QW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_W_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_DW_ARG || + curr->op == asBC_FREE) && // FREE both read and write to the variable + int(curr->wArg[0]) == offset ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG) && + int(curr->wArg[1]) == offset ) + return true; + else if( asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG && + (int(curr->wArg[0]) == offset || int(curr->wArg[1]) == offset) ) + return true; + else if( curr->op == asBC_LoadThisR && offset == 0 ) + return true; + + return false; +} + +bool asCByteCode::IsInstrJmpOrLabel(asCByteInstruction *curr) +{ + if( curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP || + curr->op == asBC_JMPP || + curr->op == asBC_JMP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_JLowZ || + curr->op == asBC_JLowNZ || + curr->op == asBC_LABEL ) + return true; + + return false; +} + +bool asCByteCode::IsTempVarOverwrittenByInstr(asCByteInstruction *curr, int offset) +{ + // Which instructions overwrite the variable or discard it? + if( curr->op == asBC_RET || + curr->op == asBC_SUSPEND ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG) && + int(curr->wArg[0]) == offset ) + return true; + + return false; +} + +bool asCByteCode::IsTempVarRead(asCByteInstruction *curr, int offset) +{ + TimeIt("asCByteCode::IsTempVarRead"); + + asCArray openPaths; + asCArray closedPaths; + + // We're not interested in the first instruction, since it is the one that sets the variable + openPaths.PushLast(curr->next); + + while( openPaths.GetLength() ) + { + curr = openPaths.PopLast(); + + // Add the instruction to the closed paths so that we don't verify it again + closedPaths.PushLast(curr); + + while( curr ) + { + if( IsTempVarReadByInstr(curr, offset) ) + return true; + + if( IsTempVarOverwrittenByInstr(curr, offset) ) break; + + // In case of jumps, we must follow the each of the paths + if( curr->op == asBC_JMP ) + { + // Find the destination. If it cannot be found it is because we're doing a localized + // optimization and the label hasn't been added to the final bytecode yet + + int label = *((int*)ARG_DW(curr->arg)); + int r = FindLabel(label, curr, &curr, 0); + if( r >= 0 && + !closedPaths.Exists(curr) && + !openPaths.Exists(curr) ) + openPaths.PushLast(curr); + + break; + } + else if( curr->op == asBC_JZ || curr->op == asBC_JNZ || + curr->op == asBC_JS || curr->op == asBC_JNS || + curr->op == asBC_JP || curr->op == asBC_JNP || + curr->op == asBC_JLowZ || curr->op == asBC_JLowNZ ) + { + // Find the destination. If it cannot be found it is because we're doing a localized + // optimization and the label hasn't been added to the final bytecode yet + + asCByteInstruction *dest = 0; + int label = *((int*)ARG_DW(curr->arg)); + int r = FindLabel(label, curr, &dest, 0); + if( r >= 0 && + !closedPaths.Exists(dest) && + !openPaths.Exists(dest) ) + openPaths.PushLast(dest); + } + else if( curr->op == asBC_JMPP ) + { + // A JMPP instruction is always followed by a series of JMP instructions + // that give the real destination (like a look-up table). We need add all + // of these as open paths. + curr = curr->next; + while( curr->op == asBC_JMP ) + { + // Find the destination. If it cannot be found it is because we're doing a localized + // optimization and the label hasn't been added to the final bytecode yet + + asCByteInstruction *dest = 0; + int label = *((int*)ARG_DW(curr->arg)); + int r = FindLabel(label, curr, &dest, 0); + if( r >= 0 && + !closedPaths.Exists(dest) && + !openPaths.Exists(dest) ) + openPaths.PushLast(dest); + + curr = curr->next; + } + + // We should now be on a label which is the destination of the + // first JMP in the sequence and is already added in the open paths + asASSERT(curr->op == asBC_LABEL); + break; + } + + curr = curr->next; + } + } + + return false; +} + +bool asCByteCode::IsTempRegUsed(asCByteInstruction *curr) +{ + TimeIt("asCByteCode::IsTempRegUsed"); + + // We're not interested in the first instruction, since it is the one that sets the register + while( curr->next ) + { + curr = curr->next; + + // Which instructions read from the register? + if( curr->op == asBC_INCi || + curr->op == asBC_INCi16 || + curr->op == asBC_INCi8 || + curr->op == asBC_INCf || + curr->op == asBC_INCd || + curr->op == asBC_DECi || + curr->op == asBC_DECi16 || + curr->op == asBC_DECi8 || + curr->op == asBC_DECf || + curr->op == asBC_DECd || + curr->op == asBC_WRTV1 || + curr->op == asBC_WRTV2 || + curr->op == asBC_WRTV4 || + curr->op == asBC_WRTV8 || + curr->op == asBC_RDR1 || + curr->op == asBC_RDR2 || + curr->op == asBC_RDR4 || + curr->op == asBC_RDR8 || + curr->op == asBC_PshRPtr || + curr->op == asBC_CpyRtoV4 || + curr->op == asBC_CpyRtoV8 || + curr->op == asBC_TZ || + curr->op == asBC_TNZ || + curr->op == asBC_TS || + curr->op == asBC_TNS || + curr->op == asBC_TP || + curr->op == asBC_TNP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_JLowZ || + curr->op == asBC_JLowNZ || + curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP ) + return true; + + // Which instructions overwrite the register or discard the value? + if( curr->op == asBC_CALL || + curr->op == asBC_PopRPtr || + curr->op == asBC_CALLSYS || + curr->op == asBC_CALLBND || + curr->op == asBC_SUSPEND || + curr->op == asBC_ALLOC || + curr->op == asBC_CpyVtoR4 || + curr->op == asBC_LdGRdR4 || + curr->op == asBC_LDG || + curr->op == asBC_LDV || + curr->op == asBC_TZ || + curr->op == asBC_TNZ || + curr->op == asBC_TS || + curr->op == asBC_TNS || + curr->op == asBC_TP || + curr->op == asBC_TNP || + curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP || + curr->op == asBC_JMPP || + curr->op == asBC_JMP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_JLowZ || + curr->op == asBC_JLowNZ || + curr->op == asBC_CMPi || + curr->op == asBC_CMPu || + curr->op == asBC_CMPf || + curr->op == asBC_CMPd || + curr->op == asBC_CMPIi || + curr->op == asBC_CMPIu || + curr->op == asBC_CMPIf || + curr->op == asBC_LABEL || + curr->op == asBC_LoadThisR || + curr->op == asBC_LoadRObjR || + curr->op == asBC_LoadVObjR ) + return false; + } + + return false; +} + +bool asCByteCode::IsSimpleExpression() +{ + // A simple expression is one that cannot be suspended at any time, i.e. + // it doesn't have any calls to other routines, and doesn't have any suspend instructions + asCByteInstruction *instr = first; + while( instr ) + { + if( instr->op == asBC_ALLOC || + instr->op == asBC_CALL || + instr->op == asBC_CALLSYS || + instr->op == asBC_SUSPEND || + instr->op == asBC_LINE || + instr->op == asBC_FREE || + instr->op == asBC_CallPtr || + instr->op == asBC_CALLINTF || + instr->op == asBC_CALLBND ) + return false; + + instr = instr->next; + } + + return true; +} + +void asCByteCode::ExtractLineNumbers() +{ + // This function will extract the line number and source file for each statement by looking for LINE instructions. + // The LINE instructions will be converted to SUSPEND instructions, or removed depending on the configuration. + + TimeIt("asCByteCode::ExtractLineNumbers"); + + int lastLinePos = -1; + int pos = 0; + asCByteInstruction *instr = first; + while( instr ) + { + asCByteInstruction *curr = instr; + instr = instr->next; + + if( curr->op == asBC_LINE ) + { + if( lastLinePos == pos ) + { + lineNumbers.PopLast(); // pop position + lineNumbers.PopLast(); // pop line number + sectionIdxs.PopLast(); // pop section index + } + + lastLinePos = pos; + lineNumbers.PushLast(pos); + lineNumbers.PushLast(*(int*)ARG_DW(curr->arg)); + sectionIdxs.PushLast(*((int*)ARG_DW(curr->arg)+1)); + + if( !engine->ep.buildWithoutLineCues ) + { + // Transform BC_LINE into BC_SUSPEND + curr->op = asBC_SUSPEND; + curr->size = asBCTypeSize[asBCInfo[asBC_SUSPEND].type]; + pos += curr->size; + } + else + { + // Delete the instruction + DeleteInstruction(curr); + } + } + else + pos += curr->size; + } +} + +void asCByteCode::ExtractObjectVariableInfo(asCScriptFunction *outFunc) +{ + asASSERT( outFunc->scriptData ); + + unsigned int pos = 0; + asCByteInstruction *instr = first; + int blockLevel = 0; + while( instr ) + { + if( instr->op == asBC_Block ) + { + asSObjectVariableInfo info; + info.programPos = pos; + info.variableOffset = 0; + info.option = instr->wArg[0] ? asBLOCK_BEGIN : asBLOCK_END; + if( info.option == asBLOCK_BEGIN ) + { + blockLevel++; + outFunc->scriptData->objVariableInfo.PushLast(info); + } + else + { + blockLevel--; + asASSERT( blockLevel >= 0 ); + if( outFunc->scriptData->objVariableInfo[outFunc->scriptData->objVariableInfo.GetLength()-1].option == asBLOCK_BEGIN && + outFunc->scriptData->objVariableInfo[outFunc->scriptData->objVariableInfo.GetLength()-1].programPos == pos ) + outFunc->scriptData->objVariableInfo.PopLast(); + else + outFunc->scriptData->objVariableInfo.PushLast(info); + } + } + else if( instr->op == asBC_ObjInfo ) + { + asSObjectVariableInfo info; + info.programPos = pos; + info.variableOffset = (short)instr->wArg[0]; + info.option = (asEObjVarInfoOption)*(int*)ARG_DW(instr->arg); + outFunc->scriptData->objVariableInfo.PushLast(info); + } + else if( instr->op == asBC_VarDecl ) + { + outFunc->scriptData->variables[instr->wArg[0]]->declaredAtProgramPos = pos; + } + else + pos += instr->size; + + instr = instr->next; + } + asASSERT( blockLevel == 0 ); +} + +int asCByteCode::GetSize() +{ + int size = 0; + asCByteInstruction *instr = first; + while( instr ) + { + size += instr->GetSize(); + + instr = instr->next; + } + + return size; +} + +void asCByteCode::AddCode(asCByteCode *bc) +{ + if( bc == this ) return; + if( bc->first ) + { + if( first == 0 ) + { + first = bc->first; + last = bc->last; + bc->first = 0; + bc->last = 0; + } + else + { + last->next = bc->first; + bc->first->prev = last; + last = bc->last; + bc->first = 0; + bc->last = 0; + } + } +} + +int asCByteCode::AddInstruction() +{ + void *ptr = engine->memoryMgr.AllocByteInstruction(); + if( ptr == 0 ) + { + // Out of memory + return 0; + } + + asCByteInstruction *instr = new(ptr) asCByteInstruction(); + if( first == 0 ) + { + first = last = instr; + } + else + { + last->AddAfter(instr); + last = instr; + } + + return 0; +} + +int asCByteCode::AddInstructionFirst() +{ + void *ptr = engine->memoryMgr.AllocByteInstruction(); + if( ptr == 0 ) + { + // Out of memory + return 0; + } + + asCByteInstruction *instr = new(ptr) asCByteInstruction(); + if( first == 0 ) + { + first = last = instr; + } + else + { + first->AddBefore(instr); + first = instr; + } + + return 0; +} + +void asCByteCode::Call(asEBCInstr instr, int funcID, int pop) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[instr].type == asBCTYPE_DW_ARG); + + last->op = instr; + last->size = asBCTypeSize[asBCInfo[instr].type]; + last->stackInc = -pop; // BC_CALL and BC_CALLBND doesn't pop the argument but when the callee returns the arguments are already popped + *((int*)ARG_DW(last->arg)) = funcID; + + // Add a JitEntry instruction after function calls so that JIT's can resume execution + InstrPTR(asBC_JitEntry, 0); +} + +void asCByteCode::CallPtr(asEBCInstr instr, int funcPtrVar, int pop) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[instr].type == asBCTYPE_rW_ARG); + + last->op = instr; + last->size = asBCTypeSize[asBCInfo[instr].type]; + last->stackInc = -pop; + last->wArg[0] = (short)funcPtrVar; + + // Add a JitEntry instruction after function calls so that JIT's can resume execution + InstrPTR(asBC_JitEntry, 0); +} + +void asCByteCode::Alloc(asEBCInstr instr, void *objID, int funcID, int pop) +{ + if( AddInstruction() < 0 ) + return; + + last->op = instr; + last->size = asBCTypeSize[asBCInfo[instr].type]; + last->stackInc = -pop; // BC_ALLOC + + asASSERT(asBCInfo[instr].type == asBCTYPE_PTR_DW_ARG); + *ARG_PTR(last->arg) = (asPWORD)objID; + *((int*)(ARG_DW(last->arg)+AS_PTR_SIZE)) = funcID; + + // Add a JitEntry instruction after function calls so that JIT's can resume execution + InstrPTR(asBC_JitEntry, 0); +} + +void asCByteCode::Ret(int pop) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[asBC_RET].type == asBCTYPE_W_ARG); + + last->op = asBC_RET; + last->size = asBCTypeSize[asBCInfo[asBC_RET].type]; + last->stackInc = 0; // The instruction pops the argument, but it doesn't affect current function + last->wArg[0] = (short)pop; +} + +void asCByteCode::JmpP(int var, asDWORD max) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[asBC_JMPP].type == asBCTYPE_rW_ARG); + + last->op = asBC_JMPP; + last->size = asBCTypeSize[asBCInfo[asBC_JMPP].type]; + last->stackInc = asBCInfo[asBC_JMPP].stackInc; + last->wArg[0] = (short)var; + + // Store the largest jump that is made for PostProcess() + *ARG_DW(last->arg) = max; +} + +void asCByteCode::Label(short label) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_LABEL; + last->size = 0; + last->stackInc = 0; + last->wArg[0] = label; +} + +void asCByteCode::Line(int line, int column, int scriptIdx) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_LINE; + // If the build is without line cues these instructions will be removed + // otherwise they will be transformed into SUSPEND instructions. + if( engine->ep.buildWithoutLineCues ) + last->size = 0; + else + last->size = asBCTypeSize[asBCInfo[asBC_SUSPEND].type]; + last->stackInc = 0; + *((int*)ARG_DW(last->arg)) = (line & 0xFFFFF)|((column & 0xFFF)<<20); + *((int*)ARG_DW(last->arg)+1) = scriptIdx; + + // Add a JitEntry after the line instruction to allow the JIT function to resume after a suspend + InstrPTR(asBC_JitEntry, 0); +} + +void asCByteCode::ObjInfo(int offset, int info) +{ + if( AddInstruction() < 0 ) + return; + + // Add the special instruction that will be used to tell the exception + // handler when an object is initialized and deinitialized. + last->op = asBC_ObjInfo; + last->size = 0; + last->stackInc = 0; + last->wArg[0] = (short)offset; + *((int*)ARG_DW(last->arg)) = info; +} + +void asCByteCode::Block(bool start) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_Block; + last->size = 0; + last->stackInc = 0; + last->wArg[0] = start ? 1 : 0; +} + +void asCByteCode::VarDecl(int varDeclIdx) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_VarDecl; + last->size = 0; + last->stackInc = 0; + last->wArg[0] = asWORD(varDeclIdx); +} + +int asCByteCode::FindLabel(int label, asCByteInstruction *from, asCByteInstruction **dest, int *positionDelta) +{ + TimeIt("asCByteCode::FindLabel"); + + // Search forward + int labelPos = -from->GetSize(); + + asCByteInstruction *labelInstr = from; + while( labelInstr ) + { + labelPos += labelInstr->GetSize(); + labelInstr = labelInstr->next; + + if( labelInstr && labelInstr->op == asBC_LABEL ) + { + if( labelInstr->wArg[0] == label ) + break; + } + } + + if( labelInstr == 0 ) + { + // Search backwards + labelPos = -from->GetSize(); + + labelInstr = from; + while( labelInstr ) + { + labelInstr = labelInstr->prev; + if( labelInstr ) + { + labelPos -= labelInstr->GetSize(); + + if( labelInstr->op == asBC_LABEL ) + { + if( labelInstr->wArg[0] == label ) + break; + } + } + } + } + + if( labelInstr != 0 ) + { + if( dest ) *dest = labelInstr; + if( positionDelta ) *positionDelta = labelPos; + return 0; + } + + return -1; +} + +int asCByteCode::ResolveJumpAddresses() +{ + TimeIt("asCByteCode::ResolveJumpAddresses"); + + asCByteInstruction *instr = first; + while( instr ) + { + if( instr->op == asBC_JMP || + instr->op == asBC_JZ || instr->op == asBC_JNZ || + instr->op == asBC_JLowZ || instr->op == asBC_JLowNZ || + instr->op == asBC_JS || instr->op == asBC_JNS || + instr->op == asBC_JP || instr->op == asBC_JNP ) + { + int label = *((int*) ARG_DW(instr->arg)); + int labelPosOffset; + int r = FindLabel(label, instr, 0, &labelPosOffset); + if( r == 0 ) + *((int*) ARG_DW(instr->arg)) = labelPosOffset; + else + return -1; + } + + instr = instr->next; + } + + return 0; +} + + +asCByteInstruction *asCByteCode::DeleteInstruction(asCByteInstruction *instr) +{ + if( instr == 0 ) return 0; + + asCByteInstruction *ret = instr->prev ? instr->prev : instr->next; + + RemoveInstruction(instr); + + engine->memoryMgr.FreeByteInstruction(instr); + + return ret; +} + +void asCByteCode::Output(asDWORD *array) +{ + TimeIt("asCByteCode::Output"); + + // TODO: Receive a script function pointer instead of the bytecode array + + asDWORD *ap = array; + + asCByteInstruction *instr = first; + while( instr ) + { + if( instr->GetSize() > 0 ) + { + *(asBYTE*)ap = asBYTE(instr->op); + *(((asBYTE*)ap)+1) = 0; // Second byte is always zero + switch( asBCInfo[instr->op].type ) + { + case asBCTYPE_NO_ARG: + *(((asWORD*)ap)+1) = 0; // Clear upper bytes + break; + case asBCTYPE_wW_rW_rW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(((asWORD*)ap)+2) = instr->wArg[1]; + *(((asWORD*)ap)+3) = instr->wArg[2]; + break; + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_W_DW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(ap+1) = *(asDWORD*)&instr->arg; + break; + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_W_DW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(((asWORD*)ap)+2) = instr->wArg[1]; + *(ap+2) = *(asDWORD*)&instr->arg; + break; + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_QW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(asQWORD*)(ap+1) = asQWORD(instr->arg); + break; + case asBCTYPE_W_ARG: + case asBCTYPE_rW_ARG: + case asBCTYPE_wW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + break; + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + case asBCTYPE_wW_W_ARG: + *(((asWORD *)ap)+1) = instr->wArg[0]; + *(((asWORD *)ap)+2) = instr->wArg[1]; + break; + case asBCTYPE_QW_DW_ARG: + case asBCTYPE_DW_DW_ARG: + case asBCTYPE_QW_ARG: + case asBCTYPE_DW_ARG: + *(((asWORD*)ap)+1) = 0; // Clear upper bytes + memcpy(ap+1, &instr->arg, instr->GetSize()*4-4); + break; + case asBCTYPE_rW_DW_DW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + memcpy(ap+1, &instr->arg, instr->GetSize()*4-4); + break; + default: + // How did we get here? + asASSERT(false); + break; + } + } + + ap += instr->GetSize(); + instr = instr->next; + } +} + +void asCByteCode::PostProcess() +{ + TimeIt("asCByteCode::PostProcess"); + + if( first == 0 ) return; + + // This function will do the following + // - Verify if there is any code that never gets executed and remove it + // - Calculate the stack size at the position of each byte code + // - Calculate the largest stack needed + + largestStackUsed = 0; + + asCByteInstruction *instr = first; + while( instr ) + { + instr->marked = false; + instr->stackSize = -1; + instr = instr->next; + } + + // Add the first instruction to the list of unchecked code paths + asCArray paths; + AddPath(paths, first, 0); + + // Go through each of the code paths + for( asUINT p = 0; p < paths.GetLength(); ++p ) + { + instr = paths[p]; + int stackSize = instr->stackSize; + + while( instr ) + { + instr->marked = true; + instr->stackSize = stackSize; + stackSize += instr->stackInc; + if( stackSize > largestStackUsed ) + largestStackUsed = stackSize; + + if( instr->op == asBC_JMP ) + { + // Find the label that we should jump to + int label = *((int*) ARG_DW(instr->arg)); + asCByteInstruction *dest = 0; + int r = FindLabel(label, instr, &dest, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + AddPath(paths, dest, stackSize); + break; + } + else if( instr->op == asBC_JZ || instr->op == asBC_JNZ || + instr->op == asBC_JLowZ || instr->op == asBC_JLowNZ || + instr->op == asBC_JS || instr->op == asBC_JNS || + instr->op == asBC_JP || instr->op == asBC_JNP ) + { + // Find the label that is being jumped to + int label = *((int*) ARG_DW(instr->arg)); + asCByteInstruction *dest = 0; + int r = FindLabel(label, instr, &dest, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + AddPath(paths, dest, stackSize); + + // Add both paths to the code paths + AddPath(paths, instr->next, stackSize); + + break; + } + else if( instr->op == asBC_JMPP ) + { + // I need to know the largest value possible + asDWORD max = *ARG_DW(instr->arg); + + // Add all destinations to the code paths + asCByteInstruction *dest = instr->next; + for( asDWORD n = 0; n <= max && dest != 0; ++n ) + { + AddPath(paths, dest, stackSize); + dest = dest->next; + } + + break; + } + else + { + instr = instr->next; + if( instr == 0 || instr->marked ) + break; + } + } + } + + // Are there any instructions that didn't get visited? + instr = first; + while( instr ) + { + // Don't remove asBC_Block instructions as then the start and end of blocks may become mismatched + if( instr->marked == false && instr->op != asBC_Block ) + { + // Remove it + asCByteInstruction *curr = instr; + instr = instr->next; + DeleteInstruction(curr); + } + else + { +#ifndef AS_DEBUG + // If the stackSize is negative, then there is a problem with the bytecode. + // If AS_DEBUG is turned on, this same check is done in DebugOutput. + asASSERT( instr->stackSize >= 0 || asBCInfo[instr->op].type == asBCTYPE_INFO ); +#endif + instr = instr->next; + } + } +} + +#ifdef AS_DEBUG +void asCByteCode::DebugOutput(const char *name, asCScriptFunction *func) +{ + _mkdir("AS_DEBUG"); + + asCString path = "AS_DEBUG/"; + path += name; + + // Anonymous functions created from within class methods will contain :: as part of the name + // Replace :: with __ to avoid error when creating the file for debug output + for (asUINT n = 0; n < path.GetLength(); n++) + if (path[n] == ':') path[n] = '_'; + +#if _MSC_VER >= 1500 && !defined(AS_MARMALADE) + FILE *file; + fopen_s(&file, path.AddressOf(), "w"); +#else + FILE *file = fopen(path.AddressOf(), "w"); +#endif + +#if !defined(AS_XENON) // XBox 360: When running in DVD Emu, no write is allowed + asASSERT( file ); +#endif + + if( file == 0 ) + return; + + asUINT n; + + fprintf(file, "%s\n\n", func->GetDeclaration()); + + fprintf(file, "Temps: "); + for( n = 0; n < temporaryVariables->GetLength(); n++ ) + { + fprintf(file, "%d", (*temporaryVariables)[n]); + if( n < temporaryVariables->GetLength()-1 ) + fprintf(file, ", "); + } + fprintf(file, "\n\n"); + + fprintf(file, "Variables: \n"); + for( n = 0; n < func->scriptData->variables.GetLength(); n++ ) + { + int idx = func->scriptData->objVariablePos.IndexOf(func->scriptData->variables[n]->stackOffset); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s %s\n", func->scriptData->variables[n]->stackOffset, isOnHeap ? "(heap) " : "", func->scriptData->variables[n]->type.Format(func->nameSpace).AddressOf(), func->scriptData->variables[n]->name.AddressOf()); + } + asUINT offset = 0; + if( func->objectType ) + { + fprintf(file, " %.3d: %s this\n", 0, func->objectType->name.AddressOf()); + offset -= AS_PTR_SIZE; + } + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + bool found = false; + for( asUINT v = 0; v < func->scriptData->variables.GetLength(); v++ ) + { + if( func->scriptData->variables[v]->stackOffset == (int)offset ) + { + found = true; + break; + } + } + if( !found ) + { + int idx = func->scriptData->objVariablePos.IndexOf(offset); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s {noname param}\n", offset, isOnHeap ? "(heap) " : "", func->parameterTypes[n].Format(func->nameSpace).AddressOf()); + } + + offset -= func->parameterTypes[n].GetSizeOnStackDWords(); + } + for( n = 0; n < func->scriptData->objVariablePos.GetLength(); n++ ) + { + bool found = false; + for( asUINT v = 0; v < func->scriptData->variables.GetLength(); v++ ) + { + if( func->scriptData->variables[v]->stackOffset == func->scriptData->objVariablePos[n] ) + { + found = true; + break; + } + } + if( !found ) + { + if( func->scriptData->objVariableTypes[n] ) + { + int idx = func->scriptData->objVariablePos.IndexOf(func->scriptData->objVariablePos[n]); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s {noname}\n", func->scriptData->objVariablePos[n], isOnHeap ? "(heap) " : "", func->scriptData->objVariableTypes[n]->name.AddressOf()); + } + else + fprintf(file, " %.3d: null handle {noname}\n", func->scriptData->objVariablePos[n]); + } + } + fprintf(file, "\n\n"); + + bool invalidStackSize = false; + int pos = 0; + asUINT lineIndex = 0; + asCByteInstruction *instr = first; + while( instr ) + { + if( lineIndex < lineNumbers.GetLength() && lineNumbers[lineIndex] == pos ) + { + asDWORD line = lineNumbers[lineIndex+1]; + fprintf(file, "- %d,%d -\n", (int)(line&0xFFFFF), (int)(line>>20)); + lineIndex += 2; + } + + if( instr->GetSize() > 0 ) + { + fprintf(file, "%5d ", pos); + pos += instr->GetSize(); + + fprintf(file, "%3d %c ", int(instr->stackSize + func->scriptData->variableSpace), instr->marked ? '*' : ' '); + if( instr->stackSize < 0 ) + invalidStackSize = true; + } + else + { + fprintf(file, " "); + } + + switch( asBCInfo[instr->op].type ) + { + case asBCTYPE_W_ARG: + if( instr->op == asBC_STR ) + { + int id = asWORD(instr->wArg[0]); + const asCString &str = engine->GetConstantString(id); + fprintf(file, " %-8s %d (l:%ld s:\"%.10s\")\n", asBCInfo[instr->op].name, asWORD(instr->wArg[0]), (long int)str.GetLength(), str.AddressOf()); + } + else + fprintf(file, " %-8s %d\n", asBCInfo[instr->op].name, instr->wArg[0]); + break; + + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_ARG: + fprintf(file, " %-8s v%d\n", asBCInfo[instr->op].name, instr->wArg[0]); + break; + + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + fprintf(file, " %-8s v%d, v%d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1]); + break; + + case asBCTYPE_wW_W_ARG: + fprintf(file, " %-8s v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1]); + break; + + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_W_DW_ARG: + switch( instr->op ) + { + case asBC_ADDIf: + case asBC_SUBIf: + case asBC_MULIf: + fprintf(file, " %-8s v%d, v%d, %f\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], *((float*) ARG_DW(instr->arg))); + break; + default: + fprintf(file, " %-8s v%d, v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], *((int*) ARG_DW(instr->arg))); + break; + } + break; + + case asBCTYPE_DW_ARG: + switch( instr->op ) + { + case asBC_OBJTYPE: + { + asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), ot->GetName()); + } + break; + + case asBC_FuncPtr: + { + asCScriptFunction *f = *(asCScriptFunction**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), f->GetDeclaration()); + } + break; + + case asBC_PshC4: + case asBC_Cast: + fprintf(file, " %-8s 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); + break; + + case asBC_TYPEID: + fprintf(file, " %-8s 0x%x '%s'\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), engine->GetTypeDeclaration((int)*ARG_DW(instr->arg))); + break; + + case asBC_CALL: + case asBC_CALLSYS: + case asBC_CALLBND: + case asBC_CALLINTF: + case asBC_Thiscall1: + { + int funcID = *(int*)ARG_DW(instr->arg); + asCString decl = engine->GetFunctionDeclaration(funcID); + + fprintf(file, " %-8s %d (%s)\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg)), decl.AddressOf()); + } + break; + + case asBC_REFCPY: + fprintf(file, " %-8s 0x%x\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg))); + break; + + case asBC_JMP: + case asBC_JZ: + case asBC_JLowZ: + case asBC_JS: + case asBC_JP: + case asBC_JNZ: + case asBC_JLowNZ: + case asBC_JNS: + case asBC_JNP: + fprintf(file, " %-8s %+d (d:%d)\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg)), pos+*((int*) ARG_DW(instr->arg))); + break; + + default: + fprintf(file, " %-8s %d\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg))); + break; + } + break; + + case asBCTYPE_QW_ARG: + switch( instr->op ) + { + case asBC_OBJTYPE: + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), ot->GetName()); + } + break; + + case asBC_FuncPtr: + { + asCScriptFunction *f = *(asCScriptFunction**)ARG_QW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), f->GetDeclaration()); + } + break; + + default: +#ifdef __GNUC__ +#ifdef _LP64 + fprintf(file, " %-8s 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#else + fprintf(file, " %-8s 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif +#else + fprintf(file, " %-8s 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif + } + break; + + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_QW_ARG: + switch( instr->op ) + { + case asBC_RefCpyV: + case asBC_FREE: + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + fprintf(file, " %-8s v%d, 0x%x (type:%s)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_QW(instr->arg), ot->GetName()); + } + break; + + default: +#ifdef __GNUC__ +#ifdef _LP64 + fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#else + fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif +#else + fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif + } + break; + + case asBCTYPE_DW_DW_ARG: + if( instr->op == asBC_ALLOC ) + { + asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + asCScriptFunction *f = engine->scriptFunctions[instr->wArg[0]]; + fprintf(file, " %-8s 0x%x, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1), ot->GetName(), f ? f->GetDeclaration() : "{no func}"); + } + else + fprintf(file, " %-8s %u, %d\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1)); + break; + + case asBCTYPE_rW_DW_DW_ARG: + fprintf(file, " %-8s v%d, %u, %u\n", asBCInfo[instr->op].name, instr->wArg[0], *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1)); + break; + + case asBCTYPE_QW_DW_ARG: + if( instr->op == asBC_ALLOC ) + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + asCScriptFunction *f = engine->scriptFunctions[instr->wArg[0]]; +#if defined(__GNUC__) && !defined(_MSC_VER) +#ifdef AS_64BIT_PTR + fprintf(file, " %-8s 0x%lx, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), f ? f->GetDeclaration() : "{no func}"); +#else + fprintf(file, " %-8s 0x%llx, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), f ? f->GetDeclaration() : "{no func}"); +#endif +#else + fprintf(file, " %-8s 0x%I64x, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), f ? f->GetDeclaration() : "{no func}"); +#endif + } + else +#if defined(__GNUC__) && !defined(_MSC_VER) +#ifdef AS_64BIT_PTR + fprintf(file, " %-8s %lu, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#else + fprintf(file, " %-8s %llu, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#endif +#else + fprintf(file, " %-8s %I64u, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#endif + break; + + case asBCTYPE_INFO: + if( instr->op == asBC_LABEL ) + fprintf(file, "%d:\n", instr->wArg[0]); + else if( instr->op == asBC_LINE ) + fprintf(file, " %s\n", asBCInfo[instr->op].name); + else if( instr->op == asBC_Block ) + fprintf(file, "%c\n", instr->wArg[0] ? '{' : '}'); + break; + + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_W_DW_ARG: + if( instr->op == asBC_SetV1 ) + fprintf(file, " %-8s v%d, 0x%x\n", asBCInfo[instr->op].name, instr->wArg[0], *(asBYTE*)ARG_DW(instr->arg)); + else if( instr->op == asBC_SetV2 ) + fprintf(file, " %-8s v%d, 0x%x\n", asBCInfo[instr->op].name, instr->wArg[0], *(asWORD*)ARG_DW(instr->arg)); + else if( instr->op == asBC_SetV4 ) + fprintf(file, " %-8s v%d, 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); + else if( instr->op == asBC_CMPIf ) + fprintf(file, " %-8s v%d, %f\n", asBCInfo[instr->op].name, instr->wArg[0], *(float*)ARG_DW(instr->arg)); + else + fprintf(file, " %-8s v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_DW(instr->arg)); + break; + + case asBCTYPE_wW_rW_rW_ARG: + fprintf(file, " %-8s v%d, v%d, v%d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], instr->wArg[2]); + break; + + case asBCTYPE_NO_ARG: + fprintf(file, " %s\n", asBCInfo[instr->op].name); + break; + + default: + asASSERT(false); + } + + instr = instr->next; + } + + fclose(file); + + // If the stackSize is negative then there is something wrong with the + // bytecode, i.e. there is a bug in the compiler or in the optimizer. We + // only check this here to have the bytecode available on file for verification + asASSERT( !invalidStackSize ); +} +#endif + +//============================================================================= + +int asCByteCode::InsertFirstInstrDWORD(asEBCInstr bc, asDWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstructionFirst() < 0 ) + return 0; + + first->op = bc; + *ARG_DW(first->arg) = param; + first->size = asBCTypeSize[asBCInfo[bc].type]; + first->stackInc = asBCInfo[bc].stackInc; + + return first->stackInc; +} + +int asCByteCode::InsertFirstInstrQWORD(asEBCInstr bc, asQWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstructionFirst() < 0 ) + return 0; + + first->op = bc; + *ARG_QW(first->arg) = param; + first->size = asBCTypeSize[asBCInfo[bc].type]; + first->stackInc = asBCInfo[bc].stackInc; + + return first->stackInc; +} + +int asCByteCode::Instr(asEBCInstr bc) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_NO_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_W_W(asEBCInstr bc, int a, int b, int c) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_rW_rW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = (short)a; + last->wArg[1] = (short)b; + last->wArg[2] = (short)c; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_W(asEBCInstr bc, int a, int b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_rW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_rW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = (short)a; + last->wArg[1] = (short)b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_PTR(asEBCInstr bc, short a, void *param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_PTR_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *ARG_PTR(last->arg) = (asPWORD)param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_DW(asEBCInstr bc, asWORD a, asDWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_W_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((int*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_DW_DW(asEBCInstr bc, short a, asDWORD b, asDWORD c) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_rW_DW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *(int*)ARG_DW(last->arg) = b; + *(int*)(ARG_DW(last->arg)+1) = c; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_B(asEBCInstr bc, short a, asBYTE b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_W_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + + // We'll have to be careful to store the byte correctly, independent of endianess. + // Some optimizing compilers may change the order of operations, so we make sure + // the value is not overwritten even if that happens. + asBYTE *argPtr = (asBYTE*)ARG_DW(last->arg); + argPtr[0] = b; // The value is always stored in the lower byte + argPtr[1] = 0; // and clear the rest of the DWORD + argPtr[2] = 0; + argPtr[3] = 0; + + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_W(asEBCInstr bc, short a, asWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_W_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + + // We'll have to be careful to store the word correctly, independent of endianess. + // Some optimizing compilers may change the order of operations, so we make sure + // the value is not overwritten even if that happens. + asWORD *argPtr = (asWORD*)ARG_DW(last->arg); + argPtr[0] = b; // The value is always stored in the lower word + argPtr[1] = 0; // and clear the rest of the DWORD + + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_DW(asEBCInstr bc, short a, asDWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_W_DW_ARG); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((int*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_QW(asEBCInstr bc, asWORD a, asQWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_QW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *ARG_QW(last->arg) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_QW(asEBCInstr bc, short a, asQWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_QW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *ARG_QW(last->arg) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_FLOAT(asEBCInstr bc, asWORD a, float b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((float*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT(asEBCInstr bc, short param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_rW_ARG || + asBCInfo[bc].type == asBCTYPE_wW_ARG || + asBCInfo[bc].type == asBCTYPE_W_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrINT(asEBCInstr bc, int param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((int*) ARG_DW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrDWORD(asEBCInstr bc, asDWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *ARG_DW(last->arg) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrPTR(asEBCInstr bc, void *param) +{ + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + asASSERT(asBCInfo[bc].type == asBCTYPE_PTR_ARG); + *ARG_PTR(last->arg) = (asPWORD)param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrQWORD(asEBCInstr bc, asQWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *ARG_QW(last->arg) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrWORD(asEBCInstr bc, asWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_W_ARG || + asBCInfo[bc].type == asBCTYPE_rW_ARG || + asBCInfo[bc].type == asBCTYPE_wW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrFLOAT(asEBCInstr bc, float param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((float*) ARG_DW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrDOUBLE(asEBCInstr bc, double param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((double*) ARG_QW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::GetLastInstr() +{ + if( last == 0 ) return -1; + + return last->op; +} + +int asCByteCode::RemoveLastInstr() +{ + if( last == 0 ) return -1; + + if( first == last ) + { + engine->memoryMgr.FreeByteInstruction(last); + first = 0; + last = 0; + } + else + { + asCByteInstruction *bc = last; + last = bc->prev; + + bc->Remove(); + engine->memoryMgr.FreeByteInstruction(bc); + } + + return 0; +} + +asDWORD asCByteCode::GetLastInstrValueDW() +{ + if( last == 0 ) return 0; + + return *ARG_DW(last->arg); +} + +//=================================================================== + +asCByteInstruction::asCByteInstruction() +{ + next = 0; + prev = 0; + + op = asBC_LABEL; + + arg = 0; + wArg[0] = 0; + wArg[1] = 0; + wArg[2] = 0; + size = 0; + stackInc = 0; + marked = false; + stackSize = 0; +} + +void asCByteInstruction::AddAfter(asCByteInstruction *nextCode) +{ + if( next ) + next->prev = nextCode; + + nextCode->next = next; + nextCode->prev = this; + next = nextCode; +} + +void asCByteInstruction::AddBefore(asCByteInstruction *prevCode) +{ + if( prev ) + prev->next = prevCode; + + prevCode->prev = prev; + prevCode->next = this; + prev = prevCode; +} + +int asCByteInstruction::GetSize() +{ + return size; +} + +int asCByteInstruction::GetStackIncrease() +{ + return stackInc; +} + +void asCByteInstruction::Remove() +{ + if( prev ) prev->next = next; + if( next ) next->prev = prev; + prev = 0; + next = 0; +} + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + diff --git a/3rdparty/angelscript/src/as_callfunc.cpp b/3rdparty/angelscript/src/as_callfunc.cpp new file mode 100644 index 0000000..15afc74 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc.cpp @@ -0,0 +1,890 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc.cpp +// +// These functions handle the actual calling of system functions +// + + + +#include "as_config.h" +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_context.h" + +BEGIN_AS_NAMESPACE + +// ref: Member Function Pointers and the Fastest Possible C++ Delegates +// describes the structure of class method pointers for most compilers +// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible + +// ref: The code comments for ItaniumCXXABI::EmitLoadOfMemberFunctionPointer in the LLVM compiler +// describes the structure for class method pointers on Itanium and arm64 ABI +// http://clang.llvm.org/doxygen/CodeGen_2ItaniumCXXABI_8cpp_source.html#l00937 + +int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *auxiliary, asSSystemFunctionInterface *internal) +{ + memset(internal, 0, sizeof(asSSystemFunctionInterface)); + + internal->func = ptr.ptr.f.func; + internal->auxiliary = 0; + + // Was a compatible calling convention specified? + if( internal->func ) + { + if( ptr.flag == 1 && callConv != asCALL_GENERIC ) + return asWRONG_CALLING_CONV; + else if( ptr.flag == 2 && (callConv == asCALL_GENERIC || callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) ) + return asWRONG_CALLING_CONV; + else if( ptr.flag == 3 && !(callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) ) + return asWRONG_CALLING_CONV; + } + + asDWORD base = callConv; + if( !isMethod ) + { + if( base == asCALL_CDECL ) + internal->callConv = ICC_CDECL; + else if( base == asCALL_STDCALL ) + internal->callConv = ICC_STDCALL; + else if( base == asCALL_THISCALL_ASGLOBAL ) + { + if(auxiliary == 0) + return asINVALID_ARG; + internal->auxiliary = auxiliary; + internal->callConv = ICC_THISCALL; + + // This is really a thiscall, so it is necessary to check for virtual method pointers + base = asCALL_THISCALL; + isMethod = true; + } + else if (base == asCALL_GENERIC) + { + internal->callConv = ICC_GENERIC_FUNC; + + // The auxiliary object is optional for generic calling convention + internal->auxiliary = auxiliary; + } + else + return asNOT_SUPPORTED; + } + + if( isMethod ) + { +#ifndef AS_NO_CLASS_METHODS + if( base == asCALL_THISCALL || base == asCALL_THISCALL_OBJFIRST || base == asCALL_THISCALL_OBJLAST ) + { + internalCallConv thisCallConv; + if( base == asCALL_THISCALL ) + { + if(callConv != asCALL_THISCALL_ASGLOBAL && auxiliary) + return asINVALID_ARG; + + thisCallConv = ICC_THISCALL; + } + else + { +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + return asNOT_SUPPORTED; +#else + if(auxiliary == 0) + return asINVALID_ARG; + + internal->auxiliary = auxiliary; + if( base == asCALL_THISCALL_OBJFIRST ) + thisCallConv = ICC_THISCALL_OBJFIRST; + else //if( base == asCALL_THISCALL_OBJLAST ) + thisCallConv = ICC_THISCALL_OBJLAST; +#endif + } + + internal->callConv = thisCallConv; +#ifdef GNU_STYLE_VIRTUAL_METHOD + if( (size_t(ptr.ptr.f.func) & 1) ) + internal->callConv = (internalCallConv)(thisCallConv + 2); +#endif + internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr); +#if (defined(AS_ARM) || defined(AS_MIPS)) && (defined(__GNUC__) || defined(AS_PSVITA)) + // As the least significant bit in func is used to switch to THUMB mode + // on ARM processors, the LSB in the __delta variable is used instead of + // the one in __pfn on ARM processors. + // MIPS also appear to use the base offset to indicate virtual method. + if( (size_t(internal->baseOffset) & 1) ) + internal->callConv = (internalCallConv)(thisCallConv + 2); +#endif + +#ifdef HAVE_VIRTUAL_BASE_OFFSET + // We don't support virtual inheritance + if( VIRTUAL_BASE_OFFSET(ptr) != 0 ) + return asNOT_SUPPORTED; +#endif + } + else +#endif + if( base == asCALL_CDECL_OBJLAST ) + internal->callConv = ICC_CDECL_OBJLAST; + else if( base == asCALL_CDECL_OBJFIRST ) + internal->callConv = ICC_CDECL_OBJFIRST; + else if (base == asCALL_GENERIC) + { + internal->callConv = ICC_GENERIC_METHOD; + internal->auxiliary = auxiliary; + } + else + return asNOT_SUPPORTED; + } + + return 0; +} + +// This function should prepare system functions so that it will be faster to call them +int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine) +{ + asASSERT(internal->callConv == ICC_GENERIC_METHOD || internal->callConv == ICC_GENERIC_FUNC); + + // Calculate the size needed for the parameters + internal->paramSize = func->GetSpaceNeededForArguments(); + + // Prepare the clean up instructions for the function arguments + internal->cleanArgs.SetLength(0); + int offset = 0; + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + asCDataType &dt = func->parameterTypes[n]; + + if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsReference() ) + { + if (dt.IsFuncdef()) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 0; // call release + clean.ot = &engine->functionBehaviours; + clean.off = short(offset); + internal->cleanArgs.PushLast(clean); + } + else if( dt.GetTypeInfo()->flags & asOBJ_REF ) + { + asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh; + asASSERT( (dt.GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release ); + if( beh->release ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 0; // call release + clean.ot = CastToObjectType(dt.GetTypeInfo()); + clean.off = short(offset); + internal->cleanArgs.PushLast(clean); + } + } + else + { + asSSystemFunctionInterface::SClean clean; + clean.op = 1; // call free + clean.ot = CastToObjectType(dt.GetTypeInfo()); + clean.off = short(offset); + + // Call the destructor then free the memory + asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh; + if( beh->destruct ) + clean.op = 2; // call destruct, then free + + internal->cleanArgs.PushLast(clean); + } + } + + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) + offset += AS_PTR_SIZE; + else + offset += dt.GetSizeOnStackDWords(); + } + + return 0; +} + +// This function should prepare system functions so that it will be faster to call them +int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine) +{ +#ifdef AS_MAX_PORTABILITY + UNUSED_VAR(func); + UNUSED_VAR(internal); + UNUSED_VAR(engine); + + // This should never happen, as when AS_MAX_PORTABILITY is on, all functions + // are asCALL_GENERIC, which are prepared by PrepareSystemFunctionGeneric + asASSERT(false); +#else + // References are always returned as primitive data + if( func->returnType.IsReference() || func->returnType.IsObjectHandle() ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = sizeof(void*)/4; + internal->hostReturnFloat = false; + } + // Registered types have special flags that determine how they are returned + else if( func->returnType.IsObject() ) + { + asDWORD objType = func->returnType.GetTypeInfo()->flags; + + // Only value types can be returned by value + asASSERT( objType & asOBJ_VALUE ); + + if( !(objType & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) ) + { + // If the return is by value then we need to know the true type + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_CANNOT_RET_TYPE_s_BY_VAL, func->returnType.GetTypeInfo()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); + } + else if( objType & asOBJ_APP_ARRAY ) + { + // Array types are always returned in memory + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + internal->hostReturnFloat = false; + } + else if( objType & asOBJ_APP_CLASS ) + { + internal->hostReturnFloat = false; + if( objType & COMPLEX_RETURN_MASK ) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } + else + { +#ifdef HAS_128_BIT_PRIMITIVES + if( func->returnType.GetSizeInMemoryDWords() > 4 ) +#else + if( func->returnType.GetSizeInMemoryDWords() > 2 ) +#endif + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } + else + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); +#ifdef SPLIT_OBJS_BY_MEMBER_TYPES + if( func->returnType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS ) + internal->hostReturnFloat = true; +#endif + } + +#ifdef THISCALL_RETURN_SIMPLE_IN_MEMORY + if((internal->callConv == ICC_THISCALL || +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + internal->callConv == ICC_VIRTUAL_THISCALL) && +#else + internal->callConv == ICC_VIRTUAL_THISCALL || + internal->callConv == ICC_THISCALL_OBJFIRST || + internal->callConv == ICC_THISCALL_OBJLAST) && +#endif + func->returnType.GetSizeInMemoryDWords() >= THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif +#ifdef CDECL_RETURN_SIMPLE_IN_MEMORY + if((internal->callConv == ICC_CDECL || + internal->callConv == ICC_CDECL_OBJLAST || + internal->callConv == ICC_CDECL_OBJFIRST) && + func->returnType.GetSizeInMemoryDWords() >= CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif +#ifdef STDCALL_RETURN_SIMPLE_IN_MEMORY + if( internal->callConv == ICC_STDCALL && + func->returnType.GetSizeInMemoryDWords() >= STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif + } + +#ifdef SPLIT_OBJS_BY_MEMBER_TYPES + // It's not safe to return objects by value because different registers + // will be used depending on the memory layout of the object. + // Ref: http://www.x86-64.org/documentation/abi.pdf + // Ref: http://www.agner.org/optimize/calling_conventions.pdf + // If the application informs that the class should be treated as all integers, then we allow it + if( !internal->hostReturnInMemory && + !(func->returnType.GetTypeInfo()->flags & (asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_ALLFLOATS)) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_DONT_SUPPORT_RET_TYPE_s_BY_VAL, func->returnType.Format(func->nameSpace).AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); + } +#endif + } + else if( objType & asOBJ_APP_PRIMITIVE ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); + internal->hostReturnFloat = false; + } + else if( objType & asOBJ_APP_FLOAT ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); + internal->hostReturnFloat = true; + } + } + // Primitive types can easily be determined +#ifdef HAS_128_BIT_PRIMITIVES + else if( func->returnType.GetSizeInMemoryDWords() > 4 ) + { + // Shouldn't be possible to get here + asASSERT(false); + } + else if( func->returnType.GetSizeInMemoryDWords() == 4 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 4; + internal->hostReturnFloat = false; + } +#else + else if( func->returnType.GetSizeInMemoryDWords() > 2 ) + { + // Shouldn't be possible to get here + asASSERT(false); + } +#endif + else if( func->returnType.GetSizeInMemoryDWords() == 2 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 2; + internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttDouble, true)); + } + else if( func->returnType.GetSizeInMemoryDWords() == 1 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 1; + internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttFloat, true)); + } + else + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 0; + internal->hostReturnFloat = false; + } + + // Calculate the size needed for the parameters + internal->paramSize = func->GetSpaceNeededForArguments(); + + // Verify if the function takes any objects by value + asUINT n; + internal->takesObjByVal = false; + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].IsObject() && !func->parameterTypes[n].IsObjectHandle() && !func->parameterTypes[n].IsReference() ) + { + internal->takesObjByVal = true; + + // Can't pass objects by value unless the application type is informed + if( !(func->parameterTypes[n].GetTypeInfo()->flags & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_CANNOT_PASS_TYPE_s_BY_VAL, func->parameterTypes[n].GetTypeInfo()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); + } + + +#ifdef SPLIT_OBJS_BY_MEMBER_TYPES + // It's not safe to pass objects by value because different registers + // will be used depending on the memory layout of the object + // Ref: http://www.x86-64.org/documentation/abi.pdf + // Ref: http://www.agner.org/optimize/calling_conventions.pdf + if( +#ifdef COMPLEX_OBJS_PASSED_BY_REF + !(func->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK) && +#endif +#ifdef LARGE_OBJS_PASS_BY_REF + func->parameterTypes[n].GetSizeInMemoryDWords() < AS_LARGE_OBJ_MIN_SIZE && +#endif + !(func->parameterTypes[n].GetTypeInfo()->flags & (asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_ALLFLOATS)) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_DONT_SUPPORT_TYPE_s_BY_VAL, func->parameterTypes[n].GetTypeInfo()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); + } +#endif + break; + } + } + + // Prepare the clean up instructions for the function arguments + internal->cleanArgs.SetLength(0); + int offset = 0; + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + asCDataType &dt = func->parameterTypes[n]; + +#if defined(COMPLEX_OBJS_PASSED_BY_REF) || defined(AS_LARGE_OBJS_PASSED_BY_REF) + bool needFree = false; +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( dt.GetTypeInfo() && dt.GetTypeInfo()->flags & COMPLEX_MASK ) needFree = true; +#endif +#ifdef AS_LARGE_OBJS_PASSED_BY_REF + if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ) needFree = true; +#endif + if( needFree && + dt.IsObject() && + !dt.IsObjectHandle() && + !dt.IsReference() ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 1; // call free + clean.ot = CastToObjectType(dt.GetTypeInfo()); + clean.off = short(offset); + +#ifndef AS_CALLEE_DESTROY_OBJ_BY_VAL + // If the called function doesn't destroy objects passed by value we must do so here + asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh; + if( beh->destruct ) + clean.op = 2; // call destruct, then free +#endif + + internal->cleanArgs.PushLast(clean); + } +#endif + + if( n < internal->paramAutoHandles.GetLength() && internal->paramAutoHandles[n] ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 0; // call release + if (dt.IsFuncdef()) + clean.ot = &engine->functionBehaviours; + else + clean.ot = CastToObjectType(dt.GetTypeInfo()); + clean.off = short(offset); + internal->cleanArgs.PushLast(clean); + } + + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) + offset += AS_PTR_SIZE; + else + offset += dt.GetSizeOnStackDWords(); + } +#endif // !defined(AS_MAX_PORTABILITY) + return 0; +} + +#ifdef AS_MAX_PORTABILITY + +int CallSystemFunction(int id, asCContext *context) +{ + asCScriptEngine *engine = context->m_engine; + asCScriptFunction *func = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = func->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(func); + + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + + return 0; +} + +#else + +// +// CallSystemFunctionNative +// +// This function is implemented for each platform where the native calling conventions is supported. +// See the various as_callfunc_xxx.cpp files for their implementation. It is responsible for preparing +// the arguments for the function call, calling the function, and then retrieving the return value. +// +// Parameters: +// +// context - This is the context that can be used to retrieve specific information from the engine +// descr - This is the script function object that holds the information on how to call the function +// obj - This is the object pointer, if the call is for a class method, otherwise it is null +// args - This is the function arguments, which are packed as in AngelScript +// retPointer - This points to a the memory buffer where the return object is to be placed, if the function returns the value in memory rather than in registers +// retQW2 - This output parameter should be used if the function returns a value larger than 64bits in registers +// secondObj - This is the object pointer that the proxy method should invoke its method on when the call convention is THISCALL_OBJFIRST/LAST +// +// Return value: +// +// The function should return the value that is returned in registers. +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObj); + + +int CallSystemFunction(int id, asCContext *context) +{ + asCScriptEngine *engine = context->m_engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(descr); + + asQWORD retQW = 0; + asQWORD retQW2 = 0; + asDWORD *args = context->m_regs.stackPointer; + void *retPointer = 0; + int popSize = sysFunc->paramSize; + +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + void *obj = 0; + void *secondObj = 0; + + if( callConv >= ICC_THISCALL ) + { + if(sysFunc->auxiliary) + { + // This class method is being called as if it is a global function + obj = sysFunc->auxiliary; + } + else + { + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + obj = (void*)*(asPWORD*)(args); + if( obj == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; + } + + // Add the base offset for multiple inheritance +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA) + // On GNUC + ARM the lsb of the offset is used to indicate a virtual function + // and the whole offset is thus shifted one bit left to keep the original + // offset resolution + // MIPS also work like ARM in this regard + obj = (void*)(asPWORD(obj) + (sysFunc->baseOffset>>1)); +#else + obj = (void*)(asPWORD(obj) + sysFunc->baseOffset); +#endif + + // Skip the object pointer + args += AS_PTR_SIZE; + } + } +#else + // TODO: clean-up: CallSystemFunctionNative should have two arguments for object pointers + // objForThiscall is the object pointer that should be used for the thiscall + // objForArg is the object pointer that should be passed as argument when using OBJFIRST or OBJLAST + + // Used to save two object pointers with THISCALL_OBJLAST or THISCALL_OBJFIRST + void *obj = 0; + void *secondObj = 0; + + if( callConv >= ICC_THISCALL ) + { + bool continueCheck = true; // True if need check objectPointer or context stack for object + int continueCheckIndex = 0; // Index into objectsPtrs to save the object if continueCheck + + if( callConv >= ICC_THISCALL_OBJLAST ) + { + asASSERT( sysFunc->auxiliary != 0 ); + // This class method is being called as object method (sysFunc->auxiliary must be set). + obj = sysFunc->auxiliary; + continueCheckIndex = 1; + } + else if(sysFunc->auxiliary) + { + // This class method is being called as if it is a global function + obj = sysFunc->auxiliary; + continueCheck = false; + } + + if( continueCheck ) + { + void *tempPtr = 0; + + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + tempPtr = (void*)*(asPWORD*)(args); + if( tempPtr == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; + } + + // Add the base offset for multiple inheritance +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || defined(AS_PSVITA) + // On GNUC + ARM the lsb of the offset is used to indicate a virtual function + // and the whole offset is thus shifted one bit left to keep the original + // offset resolution + // MIPS also work like ARM in this regard + tempPtr = (void*)(asPWORD(tempPtr) + (sysFunc->baseOffset>>1)); +#else + tempPtr = (void*)(asPWORD(tempPtr) + sysFunc->baseOffset); +#endif + + // Skip the object pointer + args += AS_PTR_SIZE; + + if( continueCheckIndex ) + secondObj = tempPtr; + else + { + asASSERT( obj == 0 ); + obj = tempPtr; + } + } + } +#endif // AS_NO_THISCALL_FUNCTOR_METHOD + + if( descr->DoesReturnOnStack() ) + { + // Get the address of the location for the return value from the stack + retPointer = (void*)*(asPWORD*)(args); + popSize += AS_PTR_SIZE; + args += AS_PTR_SIZE; + + // When returning the value on the location allocated by the called + // we shouldn't set the object type in the register + context->m_regs.objectType = 0; + } + else + { + // Set the object type of the reference held in the register + context->m_regs.objectType = descr->returnType.GetTypeInfo(); + } + + context->m_callingSystemFunction = descr; + bool cppException = false; +#ifdef AS_NO_EXCEPTIONS + retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj); +#else + // This try/catch block is to catch potential exception that may + // be thrown by the registered function. The implementation of the + // CallSystemFunctionNative() must make sure not to have any manual + // clean-up after the call to the real function, or that won't be + // executed in case of an exception. + try + { + retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj); + } + catch(...) + { + cppException = true; + + // Convert the exception to a script exception so the VM can + // properly report the error to the application and then clean up + context->SetException(TXT_EXCEPTION_CAUGHT); + } +#endif + context->m_callingSystemFunction = 0; + + // Store the returned value in our stack + if( (descr->returnType.IsObject() || descr->returnType.IsFuncdef()) && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { +#if defined(AS_BIG_ENDIAN) && AS_PTR_SIZE == 1 + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; +#endif + + context->m_regs.objectRegister = (void*)(asPWORD)retQW; + + if( sysFunc->returnAutoHandle && context->m_regs.objectRegister ) + { + asASSERT( !(descr->returnType.GetTypeInfo()->flags & asOBJ_NOCOUNT) ); + engine->CallObjectMethod(context->m_regs.objectRegister, CastToObjectType(descr->returnType.GetTypeInfo())->beh.addref); + } + } + else + { + asASSERT( retPointer ); + + if( !sysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + { +#if defined(AS_BIG_ENDIAN) && AS_PTR_SIZE == 1 + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; +#endif + + *(asDWORD*)retPointer = (asDWORD)retQW; + } + else if( sysFunc->hostReturnSize == 2 ) + *(asQWORD*)retPointer = retQW; + else if( sysFunc->hostReturnSize == 3 ) + { + *(asQWORD*)retPointer = retQW; + *(((asDWORD*)retPointer) + 2) = (asDWORD)retQW2; + } + else // if( sysFunc->hostReturnSize == 4 ) + { + *(asQWORD*)retPointer = retQW; + *(((asQWORD*)retPointer) + 1) = retQW2; + } + } + + if( context->m_status == asEXECUTION_EXCEPTION && !cppException ) + { + // If the function raised a script exception it really shouldn't have + // initialized the object. However, as it is a soft exception there is + // no way for the application to not return a value, so instead we simply + // destroy it here, to pretend it was never created. + if(CastToObjectType(descr->returnType.GetTypeInfo())->beh.destruct ) + engine->CallObjectMethod(retPointer, CastToObjectType(descr->returnType.GetTypeInfo())->beh.destruct); + } + } + } + else + { + // Store value in value register + if( sysFunc->hostReturnSize == 1 ) + { +#if defined(AS_BIG_ENDIAN) + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; + + // Due to endian issues we need to handle return values that are + // less than a DWORD (32 bits) in size specially + int numBytes = descr->returnType.GetSizeInMemoryBytes(); + if( descr->returnType.IsReference() ) numBytes = 4; + switch( numBytes ) + { + case 1: + { + // 8 bits + asBYTE *val = (asBYTE*)&context->m_regs.valueRegister; + val[0] = (asBYTE)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + val[4] = 0; + val[5] = 0; + val[6] = 0; + val[7] = 0; + } + break; + case 2: + { + // 16 bits + asWORD *val = (asWORD*)&context->m_regs.valueRegister; + val[0] = (asWORD)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + } + break; + default: + { + // 32 bits + asDWORD *val = (asDWORD*)&context->m_regs.valueRegister; + val[0] = (asDWORD)retQW; + val[1] = 0; + } + break; + } +#else + *(asDWORD*)&context->m_regs.valueRegister = (asDWORD)retQW; +#endif + } + else + context->m_regs.valueRegister = retQW; + } + + // Clean up arguments + const asUINT cleanCount = sysFunc->cleanArgs.GetLength(); + if( cleanCount ) + { + args = context->m_regs.stackPointer; + + // Skip the hidden argument for the return pointer + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( descr->DoesReturnOnStack() ) + args += AS_PTR_SIZE; + + // Skip the object pointer on the stack + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( callConv >= ICC_THISCALL && sysFunc->auxiliary == 0 ) + args += AS_PTR_SIZE; + + asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); + for( asUINT n = 0; n < cleanCount; n++, clean++ ) + { + void **addr = (void**)&args[clean->off]; + if( clean->op == 0 ) + { + if( *addr != 0 ) + { + engine->CallObjectMethod(*addr, clean->ot->beh.release); + *addr = 0; + } + } + else + { + asASSERT( clean->op == 1 || clean->op == 2 ); + asASSERT( *addr ); + + if( clean->op == 2 ) + engine->CallObjectMethod(*addr, clean->ot->beh.destruct); + + engine->CallFree(*addr); + } + } + } + + return popSize; +} + +#endif // AS_MAX_PORTABILITY + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_callfunc_arm.cpp b/3rdparty/angelscript/src/as_callfunc_arm.cpp new file mode 100644 index 0000000..6d82070 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_arm.cpp @@ -0,0 +1,665 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_arm.cpp +// +// These functions handle the actual calling of system functions on the arm platform +// +// Written by Fredrik Ehnbom in June 2009, based on as_callfunc_x86.cpp +// +// The code was complemented to support Linux with ARM by Carlos Luna in December, 2012. +// +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. + + +// This code has to conform to both AAPCS and the modified ABI for iOS +// +// Reference: +// +// AAPCS: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf +// iOS: http://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_ARM + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#if defined(AS_SOFTFP) + +// This code supports the soft-float ABI, i.e. g++ -mfloat-abi=softfp +// +// The code for iOS, Android, Marmalade and Windows Phone goes here + +BEGIN_AS_NAMESPACE + +extern "C" asQWORD armFunc (const asDWORD *, int, asFUNCTION_t); +extern "C" asQWORD armFuncR0 (const asDWORD *, int, asFUNCTION_t, asDWORD r0); +extern "C" asQWORD armFuncR0R1 (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD r1); +extern "C" asQWORD armFuncObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD obj); +extern "C" asQWORD armFuncR0ObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD obj); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + asFUNCTION_t func = sysFunc->func; + int paramSize = sysFunc->paramSize; + asFUNCTION_t *vftable; + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + + + asDWORD paramBuffer[64+2]; + // Android & Linux needs to align 64bit types on even registers, but this isn't done on iOS or Windows Phone + // TODO: optimize runtime: There should be a check for this in PrepareSystemFunction() so this + // doesn't have to be done for functions that don't have any 64bit types +#if !defined(AS_ANDROID) && !defined(AS_LINUX) + // In cases of thiscall methods, the callstack is configured as a standard thiscall + // adding the secondObject as first or last element in callstack + if( sysFunc->takesObjByVal || isThisCallMethod ) +#endif + { +#if defined(AS_ANDROID) || defined(AS_LINUX) + // mask is used as a toggler to skip uneven registers. + int mask = 1; + + if( isThisCallMethod ) + { + mask = 0; + } + else + { + // Check for object pointer as first argument + switch( callConv ) + { + case ICC_THISCALL: + case ICC_CDECL_OBJFIRST: + case ICC_VIRTUAL_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + mask = 0; + break; + default: + break; + } + } + + // Check for hidden address in case of return by value + if( sysFunc->hostReturnInMemory ) + mask = !mask; +#endif + paramSize = 0; + int spos = 0; + int dpos = 2; + + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + // TODO: runtime optimize: Declare a reference to descr->parameterTypes[n] so the array doesn't have to be access all the time + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { +#if defined(AS_ANDROID) || defined(AS_LINUX) + if( (descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8) && + ((dpos & 1) == mask) ) + { + // 64 bit value align + dpos++; + paramSize++; + } +#endif + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { +#if defined(AS_ANDROID) || defined(AS_LINUX) + // Should an alignment be performed? + if( !descr->parameterTypes[n].IsObjectHandle() && + !descr->parameterTypes[n].IsReference() && + descr->parameterTypes[n].GetSizeOnStackDWords() == 2 && + ((dpos & 1) == mask) ) + { + // 64 bit value align + dpos++; + paramSize++; + } +#endif + + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + // Add the object pointer as the last parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + // Keep a free location at the beginning + args = ¶mBuffer[2]; + } + + switch( callConv ) + { + case ICC_CDECL_RETURNINMEM: // fall through + case ICC_STDCALL_RETURNINMEM: + retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)retPointer); + break; + case ICC_CDECL: // fall through + case ICC_STDCALL: + retQW = armFunc(args, paramSize<<2, func); + break; + case ICC_THISCALL: // fall through + case ICC_CDECL_OBJFIRST: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: + retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj); + break; + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: +#ifdef __GNUC__ + // On GNUC the address where the return value will be placed should be put in R0 + retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); +#else + // On Windows the R0 should always hold the object pointer, and the address for the return value comes after + retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)obj, (asDWORD)retPointer); +#endif + break; + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: + // Get virtual function table from the object pointer + vftable = *(asFUNCTION_t**)obj; + retQW = armFuncR0(args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)obj); + break; + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asFUNCTION_t**)obj; +#ifdef __GNUC__ + // On GNUC the address where the return value will be placed should be put in R0 + retQW = armFuncR0R1(args, (paramSize+1)<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)retPointer, (asDWORD)obj); +#else + // On Windows the R0 should always hold the object pointer, and the address for the return value comes after + retQW = armFuncR0R1(args, (paramSize+1)<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)obj, (asDWORD)retPointer); +#endif + break; + case ICC_CDECL_OBJLAST: + retQW = armFuncObjLast(args, paramSize<<2, func, (asDWORD)obj); + break; + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = armFuncR0ObjLast(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + return retQW; +} + +END_AS_NAMESPACE + +#elif !defined(AS_SOFTFP) + +// This code supports the hard-float ABI, i.e. g++ -mfloat-abi=hard +// The main difference is that the floating point values are passed in the fpu registers + +#define VFP_OFFSET 70 +#define STACK_OFFSET 6 +#define PARAM_BUFFER_SIZE 104 + +BEGIN_AS_NAMESPACE + +extern "C" asQWORD armFunc (const asDWORD *, int, asFUNCTION_t); +extern "C" asQWORD armFuncR0 (const asDWORD *, int, asFUNCTION_t, asDWORD r0); +extern "C" asQWORD armFuncR0R1 (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD r1); +extern "C" asQWORD armFuncObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD obj); +extern "C" asQWORD armFuncR0ObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD obj); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + asFUNCTION_t func = sysFunc->func; + int paramSize = sysFunc->paramSize; + asFUNCTION_t *vftable; + + //---------------------------------------------------------------------------- RPi + int freeFloatSlot = VFP_OFFSET; + int freeDoubleSlot = VFP_OFFSET; + int stackPos = STACK_OFFSET; + int stackSize = 0; + //---------------------------------------------------------------------------- + + //---------------------------------------------------------------------------- RPi + // We´ll divide paramBuffer into several segments: + // + // 0-1 Unused + // 2-5 (+8 / +0 asm) values that should be placed in R0 - R3 + // 6-67 (+24 / +16 asm) values that should be placed on the stack + // 68 (+272 / +264 asm) number of values stored in r registers (R0 - R3) + // 69 (+276 / +268 asm) number of args stored on the stack + // 70-85 (+280 / +272 asm) values that should be placed in VFP registers (16) + // 86-87 (+344 / +336 asm) sp original value - sp final value - for debugging + // 88-103 (+352 / +344 asm) Check area for free-used VFP registers + // + // Total number of elements: 104 + // + // When passing the paramBuffer to the asm routines via the args pointer we are + // offsetting the start of the array to being at element # 2. That´s why in asm + // all addresses must have an offset of -2 words (-8 bytes). + //---------------------------------------------------------------------------- RPi + + asDWORD paramBuffer[PARAM_BUFFER_SIZE]; + memset(paramBuffer, 0, sizeof(asDWORD) * PARAM_BUFFER_SIZE); + + if( sysFunc->hostReturnInMemory ) + { + // TODO: runtime optimize: This check should be done in PrepareSystemFunction + if ( !( descr->returnType.GetTypeInfo()->flags & COMPLEX_RETURN_MASK ) && + ( descr->returnType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS ) && + descr->returnType.GetSizeInMemoryBytes() <= 8 ) + callConv--; + + // The return is made in memory + callConv++; + } + + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + + // Linux needs to align 64bit types on even registers, but this isn't done on iOS or Windows Phone + // TODO: optimize runtime: There should be a check for this in PrepareSystemFunction() so this + // doesn't have to be done for functions that don't have any 64bit types + { + // mask is used as a toggler to skip uneven registers. + int mask = 1; + + if( isThisCallMethod ) + { + mask = 0; + } + else + { + // Check for object pointer as first argument + switch( callConv ) + { + case ICC_THISCALL: + case ICC_CDECL_OBJFIRST: + case ICC_VIRTUAL_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + mask = 0; + break; + default: + break; + } + } + // Check for hidden address in case of return by value + if( sysFunc->hostReturnInMemory ) + mask = !mask; + + paramSize = 0; + int spos = 0; + int dpos = 2; + + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + // TODO: runtime optimize: Declare a reference to descr->parameterTypes[n] so the array doesn't have to be access all the time + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !(descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_ARRAY) ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + if( (descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8) ) + { + if ( (dpos & 1) == mask ) + { + // 64 bit value align + dpos++; + paramSize++; + } + + if ( (stackPos & 1) == mask ) + { + // 64 bit value align + stackPos++; + stackSize++; + } + } + + // Copy the object's memory to the buffer + if (descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) + { + int target = (freeFloatSlot > freeDoubleSlot) ? freeFloatSlot : freeDoubleSlot; + + if ( descr->parameterTypes[n].GetSizeInMemoryDWords() <= ( (VFP_OFFSET + 16) - target) ) + { + memcpy(¶mBuffer[target], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + memset(¶mBuffer[target + 18], (asDWORD)1, descr->parameterTypes[n].GetSizeInMemoryDWords()); + target += descr->parameterTypes[n].GetSizeInMemoryDWords(); + freeFloatSlot = freeDoubleSlot = target; + } + else + { + memcpy(¶mBuffer[stackPos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + stackPos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + stackSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + else + { + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + } + + continue; + } + else if( descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference() ) + { + // Are there any "s" registers available? + if ( freeFloatSlot < (VFP_OFFSET + 16) ) + { + if (freeFloatSlot == freeDoubleSlot) + freeDoubleSlot += 2; + + paramBuffer[freeFloatSlot + 18] = (asDWORD)1; + paramBuffer[freeFloatSlot++] = args[spos++]; + + while(freeFloatSlot < (VFP_OFFSET + 16) && paramBuffer[freeFloatSlot + 18] != 0) + freeFloatSlot++; + } + // If not, then store the float arg in the stack area + else + { + paramBuffer[stackPos++] = args[spos++]; + stackSize++; + } + + continue; + } + else if( descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference() ) + { + // Are there any "d" registers available? + if ( freeDoubleSlot < (VFP_OFFSET + 15) ) + { + if (freeFloatSlot == freeDoubleSlot) + freeFloatSlot += 2; + + // Copy two dwords for the double + paramBuffer[freeDoubleSlot + 18] = (asDWORD)1; + paramBuffer[freeDoubleSlot + 19] = (asDWORD)1; + paramBuffer[freeDoubleSlot++] = args[spos++]; + paramBuffer[freeDoubleSlot++] = args[spos++]; + + while(freeDoubleSlot < (VFP_OFFSET + 15) && paramBuffer[freeDoubleSlot + 18] != 0) + freeDoubleSlot += 2; + } + // If not, then store the double arg in the stack area + else + { + if ( (stackPos & 1) == mask ) + { + // 64 bit value align + stackPos++; + stackSize++; + } + + paramBuffer[stackPos++] = args[spos++]; + paramBuffer[stackPos++] = args[spos++]; + stackSize += 2; + } + + continue; + } + else + { + // Copy the value directly to "r" registers or the stack, checking for alignment + if (paramSize < 4) + { + // Should an alignment be performed? + if( (dpos & 1) == mask && descr->parameterTypes[n].GetSizeOnStackDWords() == 2 && + !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !descr->parameterTypes[n].IsAnyType() ) + { + // 64 bit value align + dpos++; + paramSize++; + } + + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + else + { + // Should an alignment be performed? + if( (stackPos & 1) == mask && descr->parameterTypes[n].GetSizeOnStackDWords() == 2 && + !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !descr->parameterTypes[n].IsAnyType() ) + { + // 64 bit value align + stackPos++; + stackSize++; + } + + paramBuffer[stackPos++] = args[spos++]; + stackSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + { + if (paramSize < 5) + paramBuffer[dpos++] = args[spos++]; + else + paramBuffer[stackPos++] = args[spos++]; + } + }// else... + }// Loop + + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + if (paramSize < 4) + { + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + else + { + paramBuffer[stackPos++] = (asDWORD)secondObject; + stackSize++; + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[2]; + } + + paramBuffer[69] = static_cast(stackSize<<2); + + switch( callConv ) + { + case ICC_CDECL_RETURNINMEM: // fall through + case ICC_STDCALL_RETURNINMEM: + retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)retPointer); + break; + case ICC_CDECL: // fall through + case ICC_STDCALL: + retQW = armFunc(args, paramSize<<2, func); + break; + case ICC_THISCALL: // fall through + case ICC_CDECL_OBJFIRST: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: + retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj); + break; + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: + // On GNUC the address where the return value will be placed should be put in R0 + retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); + break; + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: + // Get virtual function table from the object pointer + vftable = *(asFUNCTION_t**)obj; + retQW = armFuncR0(args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)obj); + break; + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asFUNCTION_t**)obj; + // On GNUC the address where the return value will be placed should be put in R0 + retQW = armFuncR0R1(args, (paramSize+1)<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)retPointer, (asDWORD)obj); + break; + case ICC_CDECL_OBJLAST: + retQW = armFuncObjLast(args, paramSize<<2, func, (asDWORD)obj); + break; + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = armFuncR0ObjLast(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // On Linux with arm the float and double values are returns in the + // floating point registers, s0 and s1. Objects that contain only + // float types and are not considered complex are also returned in the + // floating point registers. + if( sysFunc->hostReturnFloat ) + { + retQW = paramBuffer[VFP_OFFSET]; + + if ( sysFunc->hostReturnSize > 1 ) + retQW = *( (asQWORD*)¶mBuffer[VFP_OFFSET] ); + } + else if ( descr->returnType.IsObject() ) + { + // TODO: runtime optimize: This should be identified with a flag determined in PrepareSystemFunction + if ( !descr->returnType.IsObjectHandle() && + !descr->returnType.IsReference() && + !(descr->returnType.GetTypeInfo()->flags & COMPLEX_RETURN_MASK) && + (descr->returnType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) ) + memcpy( retPointer, ¶mBuffer[VFP_OFFSET], descr->returnType.GetSizeInMemoryBytes() ); + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_LINUX + +#endif // AS_ARM +#endif // AS_MAX_PORTABILITY + + + + diff --git a/3rdparty/angelscript/src/as_callfunc_arm_gcc.S b/3rdparty/angelscript/src/as_callfunc_arm_gcc.S new file mode 100644 index 0000000..a311ee7 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_arm_gcc.S @@ -0,0 +1,730 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +/* + Assembly routines for the ARM call convention + Written by Fredrik Ehnbom in June 2009 + + Adapted to GNUC by darktemplar216 in September 2009 + + Modified by Lasse Oorni for 8-byte stack alignment in May 2012 + + The assembler routines for Linux were written by Carlos Luna in December 2012 +*/ + +#if !defined(AS_MAX_PORTABILITY) + +#if defined(__arm__) || defined(__ARM__) || defined(I3D_ARCH_ARM) + +#if !defined(__linux__) || defined(__ANDROID__) || defined(ANDROID) || defined(__SOFTFP__) + +/* iOS, Android, Marmalade, and Linux with soft-float ABI goes here */ + +.global armFunc +.global armFuncR0 +.global armFuncR0R1 +.global armFuncObjLast +.global armFuncR0ObjLast + +/* --------------------------------------------------------------------------------------------*/ +armFunc: + stmdb sp!, {r4-r8, lr} + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r8, #0 + + beq nomoreargs + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble nomoreargs + + /* Load the rest of the arguments onto the stack */ + sub r7, r7, #4*4 /* skip the 4 registers already loaded into r0-r3 */ + add r8, r7, #4 /* ensure 8-byte stack alignment */ + bic r8, r8, #4 + sub sp, sp, r8 + mov r12, sp /* copy size != frame size, so store frame start sp */ +stackargsloop: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargsloop + mov sp, r12 +nomoreargs: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +/* --------------------------------------------------------------------------------------------*/ +armFuncObjLast: + stmdb sp!, {r4-r8, lr} + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r8, #0 + + mov r0, r3 /* objlast. might get overwritten */ + mov r5, r3 /* objlast to temp reg */ + + beq nomoreargsarmFuncObjLast + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + movlt r1, r5 + cmp r7, #3*4 + ldrge r2, [r6],#4 + movlt r2, r5 + cmp r7, #4*4 + ldrge r3, [r6],#4 + movlt r3, r5 + blt nomoreargsarmFuncObjLast + + /* Load the rest of the arguments onto the stack */ + sub r7, r7, #4*4 /* skip the 4 registers already loaded into r0-r3 */ + add r8, r7, #8 /* account for the objlast pointer, ensure 8-byte stack alignment */ + bic r8, r8, #4 + str r5, [sp,#-4] /* store the objlast on stack, twice in case we adjusted alignment */ + str r5, [sp,#-8] + sub sp, sp, r8 /* adjust frame */ + cmp r7, #0 /* we may also have come here with no extra params */ + beq nomoreargsarmFuncObjLast + mov r12, sp /* copy size != frame size, so store frame start sp */ +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncObjLast + mov sp, r12 +nomoreargsarmFuncObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0ObjLast: + stmdb sp!, {r4-r8, lr} + ldr r5, [sp,#6*4] /* objlast to temp reg */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r8, #0 + + mov r0, r3 /* r0 explicitly set */ + mov r1, r5 /* objlast. might get overwritten */ + + beq nomoreargsarmFuncR0ObjLast + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + movlt r2, r5 + cmp r7, #3*4 + ldrge r3, [r6],#4 + movlt r3, r5 + blt nomoreargsarmFuncR0ObjLast + + /* Load the rest of the arguments onto the stack */ + sub r7, r7, #3*4 /* skip the 3 registers already loaded into r1-r3 */ + add r8, r7, #8 /* account for the objlast pointer, ensure 8-byte stack alignment */ + bic r8, r8, #4 + str r5, [sp,#-4] /* store the objlast on stack, twice in case we adjusted alignment */ + str r5, [sp,#-8] + sub sp, sp, r8 /* adjust frame */ + cmp r7, #0 /* we may also have come here with no extra params */ + beq nomoreargsarmFuncR0ObjLast + mov r12, sp /* copy size != frame size, so store frame start sp */ +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0ObjLast + mov sp, r12 +nomoreargsarmFuncR0ObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0: + stmdb sp!, {r4-r8, lr} + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r8, #0 + + mov r0, r3 /* r0 explicitly set */ + + beq nomoreargsarmFuncR0 + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0 + + /* Load the rest of the arguments onto the stack */ + sub r7, r7, #3*4 /* skip the 3 registers already loaded into r1-r3 */ + add r8, r7, #4 /* ensure 8-byte stack alignment */ + bic r8, r8, #4 + sub sp, sp, r8 + mov r12, sp /* copy size != frame size, so store frame start sp */ +stackargslooparmFuncR0: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0 + mov sp, r12 +nomoreargsarmFuncR0: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0R1: + stmdb sp!, {r4-r8, lr} + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r8, #0 + + mov r0, r3 /* r0 explicitly set */ + ldr r1, [sp, #6*4] /* r1 explicitly set too */ + + beq nomoreargsarmFuncR0R1 + + /* Load the first 2 arguments into r2-r3 */ + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0R1 + + /* Load the rest of the arguments onto the stack */ + sub r7, r7, #2*4 /* skip the 2 registers already loaded into r2-r3 */ + add r8, r7, #4 /* ensure 8-byte stack alignment */ + bic r8, r8, #4 + sub sp, sp, r8 + mov r12, sp /* copy size != frame size, so store frame start sp */ +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0R1 + mov sp, r12 +nomoreargsarmFuncR0R1: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +/* --------------------------------------------------------------------------------------------*/ +#elif defined(__linux__) && !defined(__SOFTFP__) + +/* The Linux with hard-float ABI code goes here */ + + +/* These codes are suitable for armeabi + vfp / armeabihf */ +/* when using armeabi + vfp, please set C_FLAGS -mfloat-abi=softfp -mfpu=vfp */ +/* using armeabihf, please set C_FLAGS -mfloat-abi=hard -mfpu=vfpv3-d16 */ + +/* if you prefer to run in ARM mode, please add -marm to C_FLAGS */ +/* while using thumb mode, please add -mthumb -Wa,-mimplicit-it=thumb */ + + +/* SP is a multiple of 8 when control first enters a program.*/ +/* This places an obligation on authors of low level OS, RTOS, and runtime library code to align SP at all points */ +/* at which control first enters a body of (AAPCS-conforming) code. (please read "ARM IHI 0046B" document)*/ + + +.section .text + + .align 2 /* Align the function code to a 4-byte (2^n) word boundary. */ +#if defined(__thumb__) || defined(__thumb2__) + .thumb + .syntax unified +#else + .arm /* Use ARM instructions instead of Thumb.*/ +#endif + .globl armFunc /* Make the function globally accessible.*/ +armFunc: + push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + /* Load float and double args into d0-d7 and s0-s15 */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargs + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + ldrge r0, [r6] + cmp r7, #8 + ldrge r1, [r6, #4] + cmp r7, #12 + ldrge r2, [r6, #8] + cmp r7, #16 + ldrge r3, [r6, #12] + +stackargs: + ldr r5, [r6, #268] /* Load stack size into r5 */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargs + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargsloop: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargsloop + mov sp, r12 + +nomoreargs: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d7 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + +/* --------------------------------------------------------------------------------------------*/ + .align 2 /* Align the function code to a 4-byte (2^n) word boundary. */ +#if defined(__thumb__) || defined(__thumb2__) + .thumb + .syntax unified +#else + .arm /* Use ARM instructions instead of Thumb.*/ +#endif + .globl armFuncObjLast /* Make the function globally accessible.*/ +armFuncObjLast: + push {r4-r8, r10, r11, lr} /* We´re storing r11 just to keep the stack aligned to an 8 byte boundary */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 only if objlast couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncObjLast + + mov r5, r3 /* store objlast in r5 temporarily */ + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + ldrge r0, [r6] + cmp r7, #8 + ldrge r1, [r6,#4] + movlt r1, r5 + cmp r7, #12 + ldrge r2, [r6,#8] + movlt r2, r5 + cmp r7, #16 + ldrge r3, [r6,#12] + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncObjLast /* If objlast got placed into a register, go to stackargsFuncObjLast */ + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #4 /* Set r5 with an offset of #4, so objlast can be loaded into the stack */ + +stackargsFuncObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncObjLast + mov sp, r12 + +nomoreargsarmFuncObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10,r11, pc} + +/* ------------------------------------------------------------------------------------------- */ + .align 2 /* Align the function code to a 4-byte (2^n) word boundary. */ +#if defined(__thumb__) || defined(__thumb2__) + .thumb + .syntax unified +#else + .arm /* Use ARM instructions instead of Thumb.*/ +#endif + .globl armFuncR0ObjLast /* Make the function globally accessible.*/ +armFuncR0ObjLast: + push {r4-r8, r10, r11, lr} + + ldr r5, [sp,#32] /* objlast to temp reg */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* r0 explicitly set */ + mov r1, r5 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 or #8 if objlast or one arg couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncR0ObjLast + + mov r5, r1 /* store objlast in r5 temporarily */ + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + ldrge r1, [r6] + cmp r7, #8 + ldrge r2, [r6,#4] + movlt r2, r5 + cmp r7, #12 + ldrge r3, [r6,#8] + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncR0ObjLast /* If objlast got placed into a register, go to stackargsFuncR0ObjLast */ + + cmp r7, #16 /* Else if we have one last arg set the offset accordingly and store the arg in the array */ + ldrge r7, [r6, #12] + strge r7, [r6, #8] + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #0 + + movge r5, #4 /* Set r5 with an offset of #4 if there´s one last arg that couldn´t be placed in r registers */ + add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */ + +stackargsFuncR0ObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0ObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0ObjLast + mov sp, r12 + +nomoreargsarmFuncR0ObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + +/* ------------------------------------------------------------------------------------------- */ + .align 2 /* Align the function code to a 4-byte (2^n) word boundary. */ +#if defined(__thumb__) || defined(__thumb2__) + .thumb + .syntax unified +#else + .arm /* Use ARM instructions instead of Thumb.*/ +#endif + .globl armFuncR0 /* Make the function globally accessible.*/ +armFuncR0: + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 only if the last arg that should have been placed into an "r" reg needs to go to the stack */ + mov r0, r3 /* r0 explicitly set */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0 + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + ldrge r1, [r6] + cmp r7, #8 + ldrge r2, [r6, #4] + cmp r7, #12 + ldrge r3, [r6, #8] + cmp r7, #16 + movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */ + +stackargsarmFuncR0: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0 + mov sp, r12 + +nomoreargsarmFuncR0: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + +/* ------------------------------------------------------------------------------------------- */ + .align 2 /* Align the function code to a 4-byte (2^n) word boundary. */ +#if defined(__thumb__) || defined(__thumb2__) + .thumb + .syntax unified +#else + .arm /* Use ARM instructions instead of Thumb.*/ +#endif + .globl armFuncR0R1 /* Make the function globally accessible.*/ +armFuncR0R1: + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 or #8 only if the last arg (or last 2 args) that should have been placed into "r" regs need to go to the stack */ + + mov r0, r3 /* r0 explicitly set */ + ldr r1, [sp, #32] /* r1 explicitly set too */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r2-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0R1 + + /* Load the first 2 arguments into r2-r3 */ + cmp r7, #4 + ldrge r2, [r6] + cmp r7, #8 + ldrge r3, [r6, #4] + cmp r7, #12 + movge r11, #4 /* If there is a third arg to be placed, set the offset in r11 to #4 */ + cmp r7, #16 + movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */ + ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */ + strlt r7, [r6, #12] + +stackargsarmFuncR0R1: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4 or #8) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0R1 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4 or #8) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0R1 + mov sp, r12 + +nomoreargsarmFuncR0R1: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else + blx r4 +#endif + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + +#endif /* hard float abi */ + +#endif /* arm */ + +#if defined(__linux__) && defined(__ELF__) +/* ref: http://hardened.gentoo.org/gnu-stack.xml + ref: https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart */ +.section .note.GNU-stack,"",%progbits +#endif + +#endif /* !AS_MAX_PORTABILITY */ + + diff --git a/3rdparty/angelscript/src/as_callfunc_arm_msvc.asm b/3rdparty/angelscript/src/as_callfunc_arm_msvc.asm new file mode 100644 index 0000000..7ddf997 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_arm_msvc.asm @@ -0,0 +1,249 @@ +; +; AngelCode Scripting Library +; Copyright (c) 2003-2014 Andreas Jonsson +; +; 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. +; +; The original version of this library can be located at: +; http://www.angelcode.com/angelscript/ +; +; Andreas Jonsson +; andreas@angelcode.com +; + + +; Assembly routines for the ARM call convention used for Windows CE +; Written by Fredrik Ehnbom in June 2009 + +; MSVC currently doesn't support inline assembly for the ARM platform +; so this separate file is needed. + +; Compile with Microsoft ARM assembler (armasm) +; http://msdn.microsoft.com/en-us/library/hh873190.aspx + + + AREA |.rdata|, DATA, READONLY + EXPORT armFunc + EXPORT armFuncR0 + EXPORT armFuncR0R1 + EXPORT armFuncObjLast + EXPORT armFuncR0ObjLast + + AREA |.text|, CODE, ARM, ALIGN=3 + + ALIGN 8 +armFunc PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + beq |nomoreargs| + + ; Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble |nomoreargs| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #4*4 ; skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop| +|nomoreargs| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + + ALIGN 8 +armFuncObjLast PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; objlast. might get overwritten + str r3, [sp, #-4]! ; objlast again. + + beq |nomoreargs@armFuncObjLast| + + ; Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + ldrlt r1, [sp] + cmp r7, #3*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #4*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble |nomoreargs@armFuncObjLast| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #4*4 ; skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncObjLast| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncObjLast| +|nomoreargs@armFuncObjLast| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + ENDP + + ALIGN 8 +armFuncR0ObjLast PROC + stmdb sp!, {r4-r8, lr} + ldr r7, [sp,#6*4] + str r7, [sp,#-4]! + + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + ldr r1, [sp] ; objlast. might get overwritten + + beq |nomoreargs@armFuncR0ObjLast| + + ; Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #3*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble |nomoreargs@armFuncR0ObjLast| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #3*4 ; skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0ObjLast| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0ObjLast| +|nomoreargs@armFuncR0ObjLast| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + ENDP + + ALIGN 8 +armFuncR0 PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + + beq |nomoreargs@armFuncR0| + + ; Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble |nomoreargs@armFuncR0| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #3*4 ; skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0| +|nomoreargs@armFuncR0| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + + ALIGN 8 +armFuncR0R1 PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + ldr r1, [sp, #6*4] ; r1 explicitly set too + + beq |nomoreargs@armFuncR0R1| + + ; Load the first 2 arguments into r2-r3 + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble |nomoreargs@armFuncR0R1| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #2*4 ; skip the 2 registers already loaded into r2-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0R1| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0R1| +|nomoreargs@armFuncR0R1| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + + END diff --git a/3rdparty/angelscript/src/as_callfunc_arm_vita.S b/3rdparty/angelscript/src/as_callfunc_arm_vita.S new file mode 100644 index 0000000..406db03 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_arm_vita.S @@ -0,0 +1,485 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +/* + Assembly routines for the Playstation Vita SNC call convention. + + This code was adapted from as_callfunc_arm_gcc (ARM, Linux hard float) by Brandon Bare on October 2014. +*/ + +#if !defined(AS_MAX_PORTABILITY) + +#ifdef __psp2__ + +.syntax unified +.cpu cortex-a9 +.fpu neon + +.section .text.armCallFunc +.balign 2 +.thumb +.thumb_func + +.align 2 + +.global armFunc +.global armFuncR0 +.global armFuncR0R1 +.global armFuncObjLast +.global armFuncR0ObjLast + +.type armFunc, %function +.type armFuncR0, %function +.type armFuncR0R1, %function +.type armFuncObjLast, %function +.type armFuncR0ObjLast, %function + +/* --------------------------------------------------------------------------------------------*/ +armFunc: + .fnstart + + push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + /* Load float and double args into d0-d7 and s0-s15 */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargs + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + + it ge + ldrge r0, [r6] + cmp r7, #8 + + it ge + ldrge r1, [r6, #4] + cmp r7, #12 + + it ge + ldrge r2, [r6, #8] + cmp r7, #16 + + it ge + ldrge r3, [r6, #12] + +stackargs: + ldr r5, [r6, #268] /* Load stack size into r5 */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargs + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargsloop: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargsloop + mov sp, r12 + +nomoreargs: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d7 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncObjLast: + .fnstart + + push {r4-r8, r10, r11, lr} /* We´re storing r11 just to keep the stack aligned to an 8 byte boundary */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 only if objlast couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncObjLast + + mov r5, r3 /* store objlast in r5 temporarily */ + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + + it ge + ldrge r0, [r6] + cmp r7, #8 + + it ge + ldrge r1, [r6,#4] + + it lt + movlt r1, r5 + cmp r7, #12 + + it ge + ldrge r2, [r6,#8] + + it lt + movlt r2, r5 + cmp r7, #16 + + it ge + ldrge r3, [r6,#12] + + ittt lt + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncObjLast /* If objlast got placed into a register, go to stackargsFuncObjLast */ + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #4 /* Set r5 with an offset of #4, so objlast can be loaded into the stack */ + +stackargsFuncObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncObjLast + mov sp, r12 + +nomoreargsarmFuncObjLast: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10,r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0ObjLast: + .fnstart + + push {r4-r8, r10, r11, lr} + + ldr r5, [sp,#32] /* objlast to temp reg */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* r0 explicitly set */ + mov r1, r5 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 or #8 if objlast or one arg couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncR0ObjLast + + mov r5, r1 /* store objlast in r5 temporarily */ + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + + it ge + ldrge r1, [r6] + cmp r7, #8 + + it ge + ldrge r2, [r6,#4] + + it lt + movlt r2, r5 + cmp r7, #12 + + it ge + ldrge r3, [r6,#8] + + ittt lt + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncR0ObjLast /* If objlast got placed into a register, go to stackargsFuncR0ObjLast */ + + cmp r7, #16 /* Else if we have one last arg set the offset accordingly and store the arg in the array */ + + itt ge + ldrge r7, [r6, #12] + strge r7, [r6, #8] + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #0 + + it ge + movge r5, #4 /* Set r5 with an offset of #4 if there´s one last arg that couldn´t be placed in r registers */ + add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */ + +stackargsFuncR0ObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0ObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0ObjLast + mov sp, r12 + +nomoreargsarmFuncR0ObjLast: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0: + .fnstart + + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 only if the last arg that should have been placed into an "r" reg needs to go to the stack */ + mov r0, r3 /* r0 explicitly set */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0 + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + + it ge + ldrge r1, [r6] + cmp r7, #8 + + it ge + ldrge r2, [r6, #4] + cmp r7, #12 + + it ge + ldrge r3, [r6, #8] + cmp r7, #16 + + it ge + movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */ + +stackargsarmFuncR0: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0 + mov sp, r12 + +nomoreargsarmFuncR0: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0R1: + .fnstart + + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 or #8 only if the last arg (or last 2 args) that should have been placed into "r" regs need to go to the stack */ + + mov r0, r3 /* r0 explicitly set */ + ldr r1, [sp, #32] /* r1 explicitly set too */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r2-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0R1 + + /* Load the first 2 arguments into r2-r3 */ + cmp r7, #4 + + it ge + ldrge r2, [r6] + cmp r7, #8 + + it ge + ldrge r3, [r6, #4] + cmp r7, #12 + + it ge + movge r11, #4 /* If there is a third arg to be placed, set the offset in r11 to #4 */ + + cmp r7, #16 + + it ge + movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */ + + itt lt + ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */ + strlt r7, [r6, #12] + +stackargsarmFuncR0R1: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4 or #8) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0R1 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4 or #8) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0R1 + mov sp, r12 + +nomoreargsarmFuncR0R1: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +#endif + +#endif /* !AS_MAX_PORTABILITY */ + diff --git a/3rdparty/angelscript/src/as_callfunc_arm_xcode.S b/3rdparty/angelscript/src/as_callfunc_arm_xcode.S new file mode 100644 index 0000000..235e242 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_arm_xcode.S @@ -0,0 +1,242 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// Assembly routines for the ARM call convention +// Written by Fredrik Ehnbom in June 2009 + +// Adapted to GNUC by darktemplar216 in September 2009 +// Small fixed to work under XCode GCC by Gilad Novik in October 2009 + +#if !defined(AS_MAX_PORTABILITY) + +#if defined(__arm__) || defined(__ARM__) + +.align 2 +.globl _armFunc +.globl _armFuncR0 +.globl _armFuncR0R1 +.globl _armFuncObjLast +.globl _armFuncR0ObjLast + +_armFunc: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + beq nomoreargs + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble nomoreargs + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargsloop: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargsloop +nomoreargs: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +_armFuncObjLast: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // objlast. might get overwritten + str r3, [sp, #-4]! // objlast again. + + beq nomoreargsarmFuncObjLast + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + ldrlt r1, [sp] + cmp r7, #3*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #4*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncObjLast +nomoreargsarmFuncObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + +_armFuncR0ObjLast: + stmdb sp!, {r4-r8, lr} + ldr r7, [sp,#6*4] + str r7, [sp,#-4]! + + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp] // objlast. might get overwritten + + beq nomoreargsarmFuncR0ObjLast + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #3*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncR0ObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0ObjLast +nomoreargsarmFuncR0ObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + + +_armFuncR0: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + + beq nomoreargsarmFuncR0 + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0 + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0 +nomoreargsarmFuncR0: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + + +_armFuncR0R1: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp, #6*4] // r1 explicitly set too + + beq nomoreargsarmFuncR0R1 + + // Load the first 2 arguments into r2-r3 + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0R1 + + // Load the rest of the arguments onto the stack + sub r7, r7, #2*4 // skip the 2 registers already loaded into r2-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0R1 +nomoreargsarmFuncR0R1: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +#endif + +#endif /* !AS_MAX_PORTABILITY */ + diff --git a/3rdparty/angelscript/src/as_callfunc_mips.cpp b/3rdparty/angelscript/src/as_callfunc_mips.cpp new file mode 100644 index 0000000..be7d189 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_mips.cpp @@ -0,0 +1,735 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_mips.cpp +// +// These functions handle the actual calling of system functions +// +// This version is MIPS specific and was originally written +// by Manu Evans in April, 2006 for Playstation Portable (PSP) +// +// Support for Linux with MIPS was added by Andreas Jonsson in April, 2015 +// + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_MIPS + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#include +#include +#if !defined(AS_ANDROID) +#include +#endif + +BEGIN_AS_NAMESPACE + +#if defined(__linux__) && defined(_ABIO32) + +// The MIPS ABI used by Linux is implemented here +// (Tested on CI20 MIPS Creator with Debian Linux) +// +// ref: SYSTEM V +// APPLICATION BINARY INTERFACE +// MIPS RISC Processor +// http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf +// +// ref: MIPS Instruction Reference +// http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html + +union SFloatRegs +{ + union { double d0; struct { float f0; asDWORD dummy0; };}; + union { double d1; struct { float f1; asDWORD dummy1; };}; +} ; + +extern "C" asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +asDWORD GetReturnedFloat(); +asQWORD GetReturnedDouble(); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + + void *func = (void*)sysFunc->func; + void **vftable; + + asDWORD argBuffer[128]; // Ought to be big enough + asASSERT( sysFunc->paramSize < 128 ); + + asDWORD argOffset = 0; + + SFloatRegs floatRegs; + asDWORD floatOffset = 0; + + // If the application function returns the value in memory then + // the first argument must be the pointer to that memory + if( sysFunc->hostReturnInMemory ) + { + asASSERT( retPointer ); + argBuffer[argOffset++] = (asPWORD)retPointer; + } + + if( callConv == ICC_CDECL_OBJFIRST || callConv == ICC_CDECL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL || callConv == ICC_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the first argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + asCDataType ¶mType = descr->parameterTypes[n]; + if( paramType.IsObject() && !paramType.IsObjectHandle() && !paramType.IsReference() ) + { + if( paramType.GetTypeInfo()->flags & COMPLEX_MASK ) + { + // The object is passed by reference + argBuffer[argOffset++] = args[spos++]; + } + else + { + // Ensure 8byte alignment for classes that need it + if( (paramType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8) && (argOffset & 1) ) + argOffset++; + + // Copy the object's memory to the buffer + memcpy(&argBuffer[argOffset], *(void**)(args+spos), paramType.GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + argOffset += paramType.GetSizeInMemoryDWords(); + } + } + else if( paramType.GetTokenType() == ttQuestion ) + { + // Copy both pointer and type id + argBuffer[argOffset++] = args[spos++]; + argBuffer[argOffset++] = args[spos++]; + } + else + { + // The first 2 floats or doubles are loaded into the float registers. + // Actually this is only done if they are the first arguments to the function, + // but it doesn't cause any harm to load them into the registers even if they + // won't be used so we don't need to check if they really are the first args. + if( floatOffset == 0 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f0 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d0 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + else if( floatOffset == 1 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f1 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d1 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + + // Copy the value directly + if( paramType.GetSizeOnStackDWords() > 1 ) + { + // Make sure the argument is 8byte aligned + if( argOffset & 1 ) + argOffset++; + *reinterpret_cast(&argBuffer[argOffset]) = *reinterpret_cast(&args[spos]); + argOffset += 2; + spos += 2; + } + else + argBuffer[argOffset++] = args[spos++]; + } + } + + if( callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST: + case ICC_THISCALL_OBJLAST_RETURNINMEM: + retQW = mipsFunc(argOffset*4, argBuffer, func, floatRegs); + break; + + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(void***)obj; + retQW = mipsFunc(argOffset*4, argBuffer, vftable[asPWORD(func)>>2], floatRegs); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("swc1 $f0, %0\n" : "=m"(f)); + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d = 0; + + asm("sdc1 $f0, %0\n" : "=m"(d)); + + return d; +} + +// asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +// $2,$3 $4 $5 $6 $7 +asm( +" .text\n" +//" .align 2\n" +" .cfi_startproc\n" +" .global mipsFunc\n" +" .ent mipsFunc\n" +"mipsFunc:\n" +//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n" +//" .mask 0x00000000,0\n" +//" .fmask 0x00000000,0\n" +" .set noreorder\n" +" .set nomacro\n" + +// align the stack frame to 8 bytes +" addiu $12, $4, 7\n" // t4 ($12) = argSize ($4) + 7 +" li $13, -8\n" // t5 ($13) = 0xfffffffffffffff8 +" and $12, $12, $13\n" // t4 ($12) &= t5 ($13). t4 holds the size of the argument block +// It is required that the caller reserves space for at least 16 bytes even if there are less than 4 arguments +// and add 8 bytes for the return pointer and s0 ($16) backup +" addiu $13, $12, 24\n" // t5 = t4 + 24. t5 ($13) holds the total size of the stack frame (including return pointer) +// save the s0 register (so we can use it to remember where our return pointer is lives) +" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is) +" .cfi_offset 16, -4\n" +// store the return pointer +" sw $31, -8($sp)\n" +" .cfi_offset 31, -8\n" +// keep original stack pointer +" move $16, $sp\n" +" .cfi_def_cfa_register 16\n" +// push the stack +" subu $sp, $sp, $13\n" + +// store the argument in temporary registers +" addiu $25, $6, 0\n" // t9 ($25) holds the function pointer (must be t9 for position independent code) +" addiu $3, $4, 0\n" // v1 ($3) holds the size of the argument buffer +" move $15, $5\n" // t7 ($15) holds the pointer to the argBuffer +" move $14, $7\n" // t6 ($14) holds the values for the float registers + +// load integer registers +" lw $4, 0($15)\n" // a0 ($4) +" lw $5, 4($15)\n" // a1 ($5) +" lw $6, 8($15)\n" // a2 ($6) +" lw $7, 12($15)\n" // a3 ($7) + +// load float registers +" ldc1 $f12, 8($14)\n" +" ldc1 $f14, 0($14)\n" + +// skip stack parameters if there are 4 or less as they are moved into the registers +" addi $14, $3, -16\n" // The first 4 args were already loaded into registers +" blez $14, andCall\n" +" nop\n" + +// push stack parameters +"pushArgs:\n" +" addi $3, -4\n" +// load from $15 + stack bytes ($3) +" addu $14, $15, $3\n" +" lw $14, 0($14)\n" +// store to $sp + stack bytes ($3) +" addu $13, $sp, $3\n" +" sw $14, 0($13)\n" +// if there are more, loop... +" bne $3, $0, pushArgs\n" +" nop\n" + +// and call the function +"andCall:\n" +" jalr $25\n" +" nop\n" + +// restore original stack pointer +" move $sp, $16\n" +// restore the return pointer +" lw $31, -8($sp)\n" +// restore the original value of $16 +" lw $16, -4($sp)\n" +// and return from the function +" jr $31\n" +" nop\n" + +" .set macro\n" +" .set reorder\n" +" .end mipsFunc\n" +" .cfi_endproc\n" +" .size mipsFunc, .-mipsFunc\n" +); + +#else // !(defined(__linux__) && defined(_ABIO32)) + +// The MIPS ABI used by PSP and PS2 is implemented here + +#define AS_MIPS_MAX_ARGS 32 +#define AS_NUM_REG_FLOATS 8 +#define AS_NUM_REG_INTS 8 + +// The array used to send values to the correct places. +// first 0-8 regular values to load into the a0-a3, t0-t3 registers +// then 0-8 float values to load into the f12-f19 registers +// then (AS_MIPS_MAX_ARGS - 16) values to load onto the stack +// the +1 is for when CallThis (object methods) is used +// extra +1 when returning in memory +extern "C" { +// TODO: This array shouldn't be global. It should be a local array in CallSystemFunctionNative +asDWORD mipsArgs[AS_MIPS_MAX_ARGS + 1 + 1]; +} + +// Loads all data into the correct places and calls the function. +// intArgSize is the size in bytes for how much data to put in int registers +// floatArgSize is the size in bytes for how much data to put in float registers +// stackArgSize is the size in bytes for how much data to put on the callstack +extern "C" asQWORD mipsFunc(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func); + +// puts the arguments in the correct place in the mipsArgs-array. See comments above. +// This could be done better. +inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) +{ + int i; + + int argBit = 1; + for (i = 0; i < argNum; i++) + { + if (hostFlags & argBit) + { + if (numRegFloatArgs < AS_NUM_REG_FLOATS) + { + // put in float register + mipsArgs[AS_NUM_REG_INTS + numRegFloatArgs] = args[i]; + numRegFloatArgs++; + } + else + { + // put in stack + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + numRestArgs] = args[i]; + numRestArgs++; + } + } + else + { + if (numRegIntArgs < AS_NUM_REG_INTS) + { + // put in int register + mipsArgs[numRegIntArgs] = args[i]; + numRegIntArgs++; + } + else + { + // put in stack + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + numRestArgs] = args[i]; + numRestArgs++; + } + } + argBit <<= 1; + } +} + +asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the mipsArgs array + if(argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object +asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 1; + int floatArgs = 0; + int restArgs = 0; + + mipsArgs[0] = (asDWORD) obj; + + // put the arguments in the correct places in the mipsArgs array + if (argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the mipsArgs array + if(argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + if(intArgs < AS_NUM_REG_INTS) + { + mipsArgs[intArgs] = (asDWORD) obj; + intArgs++; + } + else + { + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + restArgs] = (asDWORD) obj; + restArgs++; + } + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("swc1 $f0, %0\n" : "=m"(f)); + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d = 0; + + asm("sdc1 $f0, %0\n" : "=m"(d)); + + return d; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + // TODO: Mips does not yet support THISCALL_OBJFIRST/LAST + + asQWORD retQW = 0; + + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *vftable; + + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + mipsArgs[AS_MIPS_MAX_ARGS+1] = (asDWORD) retPointer; + } + + asASSERT(descr->parameterTypes.GetLength() <= AS_MIPS_MAX_ARGS); + + // mark all float arguments + int argBit = 1; + int hostFlags = 0; + int intArgs = 0; + for( size_t a = 0; a < descr->parameterTypes.GetLength(); a++ ) + { + if (descr->parameterTypes[a].IsFloatType()) + hostFlags |= argBit; + else + intArgs++; + argBit <<= 1; + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + + +asm( +" .text\n" +//" .align 2\n" +" .global mipsFunc\n" +" .ent mipsFunc\n" +"mipsFunc:\n" +//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n" +//" .mask 0x00000000,0\n" +//" .fmask 0x00000000,0\n" +" .set noreorder\n" +" .set nomacro\n" +// align the stack frame to 8 bytes +" addiu $12, $6, 7\n" +" li $13, -8\n" // 0xfffffffffffffff8 +" and $12, $12, $13\n" // t4 holds the size of the argument block +// and add 8 bytes for the return pointer and s0 backup +" addiu $13, $12, 8\n" // t5 holds the total size of the stack frame (including return pointer) +// save the s0 register (so we can use it to remember where our return pointer is lives) +" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is) +// push the stack +" subu $sp, $sp, $13\n" +// find the return address, place in s0 +" addu $16, $sp, $12\n" +// store the return pointer +" sw $31, 0($16)\n" + +// backup our function params +" addiu $2, $7, 0\n" +" addiu $3, $6, 0\n" + +// get global mipsArgs[] array pointer +//" lui $15, %hi(mipsArgs)\n" +//" addiu $15, $15, %lo(mipsArgs)\n" +// we'll use the macro instead because SN Systems doesnt like %hi/%lo +".set macro\n" +" la $15, mipsArgs\n" +".set nomacro\n" +// load register params +" lw $4, 0($15)\n" +" lw $5, 4($15)\n" +" lw $6, 8($15)\n" +" lw $7, 12($15)\n" +" lw $8, 16($15)\n" +" lw $9, 20($15)\n" +" lw $10, 24($15)\n" +" lw $11, 28($15)\n" + +// load float params +" lwc1 $f12, 32($15)\n" +" lwc1 $f13, 36($15)\n" +" lwc1 $f14, 40($15)\n" +" lwc1 $f15, 44($15)\n" +" lwc1 $f16, 48($15)\n" +" lwc1 $f17, 52($15)\n" +" lwc1 $f18, 56($15)\n" +" lwc1 $f19, 60($15)\n" + +// skip stack paramaters if there are none +" beq $3, $0, andCall\n" + +// push stack paramaters +" addiu $15, $15, 64\n" +"pushArgs:\n" +" addiu $3, -4\n" +// load from $15 + stack bytes ($3) +" addu $14, $15, $3\n" +" lw $14, 0($14)\n" +// store to $sp + stack bytes ($3) +" addu $13, $sp, $3\n" +" sw $14, 0($13)\n" +// if there are more, loop... +" bne $3, $0, pushArgs\n" +" nop\n" + +// and call the function +"andCall:\n" +" jal $2\n" +" nop\n" + +// restore the return pointer +" lw $31, 0($16)\n" +// pop the stack pointer (remembering the return pointer was 8 bytes below the top) +" addiu $sp, $16, 8\n" +// and return from the function +" jr $31\n" +// restore the s0 register (in the branch delay slot) +" lw $16, -4($sp)\n" +" .set macro\n" +" .set reorder\n" +" .end mipsFunc\n" +" .size mipsFunc, .-mipsFunc\n" +); + +#endif // PSP and PS2 MIPS ABI + +END_AS_NAMESPACE + +#endif // AS_MIPS +#endif // AS_MAX_PORTABILITY + + + + diff --git a/3rdparty/angelscript/src/as_callfunc_ppc.cpp b/3rdparty/angelscript/src/as_callfunc_ppc.cpp new file mode 100644 index 0000000..053221f --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_ppc.cpp @@ -0,0 +1,674 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_ppc.cpp +// +// These functions handle the actual calling of system functions +// +// This version is PPC specific +// + +#include + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_PPC + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#include + +BEGIN_AS_NAMESPACE + +// This part was originally written by Pecan Heber, June 2006, for +// use on MacOS X with 32bit PPC processor. He based the code on the +// code in as_callfunc_sh4.cpp + +#define AS_PPC_MAX_ARGS 32 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register tYpe to load +// or zero if end of arguments +// The +1 is for when CallThis (object methods) is used +// Extra +1 when returning in memory +// Extra +1 in ppcArgsType to ensure zero end-of-args marker + +// TODO: multithread: We need to remove these global variables for thread-safety + +enum argTypes { ppcENDARG, ppcINTARG, ppcFLOATARG, ppcDOUBLEARG }; +static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1]; + +// Using extern "C" because we use this symbol name in the assembly code +extern "C" +{ + static asBYTE ppcArgsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1]; +} + +// NOTE: these values are for PowerPC 32 bit. +#define PPC_LINKAGE_SIZE (24) // how big the PPC linkage area is in a stack frame +#define PPC_NUM_REGSTORE (9) // how many registers of the PPC we need to store/restore for ppcFunc() +#define PPC_REGSTORE_SIZE (4*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore +#define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame +#define PPC_STACK_SIZE(numParams) (-( ( ((((numParams)<8)?8:(numParams))<<2) + EXTRA_STACK_SIZE + 15 ) & ~15 )) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes + +// Loads all data into the correct places and calls the function. +// ppcArgsType is an array containing a byte type (enum argTypes) for each argument. +// stackArgSize is the size in bytes for how much data to put on the stack frame +extern "C" asQWORD ppcFunc(const asDWORD* argsPtr, int StackArgSize, asDWORD func); + +asm(" .text\n" + " .align 2\n" // align the code to 1 << 2 = 4 bytes + " .globl _ppcFunc\n" + "_ppcFunc:\n" + + // We're receiving the following parameters + + // r3 : argsPtr + // r4 : StackArgSize + // r5 : func + + // The following registers are used through out the function + + // r31 : the address of the label address, as reference for all other labels + // r30 : temporary variable + // r29 : arg list pointer + // r28 : number of FPR registers used by the parameters + // r27 : the function pointer that will be called + // r26 : the location of the parameters for the call + // r25 : arg type list pointer + // r24 : temporary variable + // r23 : number of GPR registers used by the parameters + // r1 : this is stack pointer + // r0 : temporary variable + // f0 : temporary variable + + // We need to store some of the registers for restoral before returning to caller + + // lr - always stored in 8(r1) - this is the return address + // cr - not required to be stored, but if it is, its place is in 4(r1) - this is the condition register + // r1 - always stored in 0(r1) - this is the stack pointer + // r11 + // r13 to r31 + // f14 to f31 + + // Store register values and setup our stack frame + " mflr r0 \n" // move the return address into r0 + " stw r0, 8(r1) \n" // Store the return address on the stack + " stmw r23, -36(r1) \n" // Store registers r23 to r31 on the stack + " stwux r1, r1, r4 \n" // Increase the stack with the needed space and store the original value in the destination + + // Obtain an address that we'll use as our position of reference when obtaining addresses of other labels + " bl address \n" + "address: \n" + " mflr r31 \n" + + // initial registers for the function + " mr r29, r3 \n" // (r29) args list + " mr r27, r5 \n" // load the function pointer to call. func actually holds the pointer to our function + " addi r26, r1, 24 \n" // setup the pointer to the parameter area to the function we're going to call + " sub r0, r0, r0 \n" // zero out r0 + " mr r23, r0 \n" // zero out r23, which holds the number of used GPR registers + " mr r28, r0 \n" // zero our r22, which holds the number of used float registers + + // load the global ppcArgsType which holds the types of arguments for each argument + " addis r25, r31, ha16(_ppcArgsType - address) \n" // load the upper 16 bits of the address to r25 + " la r25, lo16(_ppcArgsType - address)(r25) \n" // load the lower 16 bits of the address to r25 + " subi r25, r25, 1 \n" // since we increment r25 on its use, we'll pre-decrement it + + // loop through the arguments + "ppcNextArg: \n" + " addi r25, r25, 1 \n" // increment r25, our arg type pointer + // switch based on the current argument type (0:end, 1:int, 2:float 3:double) + " lbz r24, 0(r25) \n" // load the current argument type (it's a byte) + " mulli r24, r24, 4 \n" // our jump table has 4 bytes per case (1 instruction) + " addis r30, r31, ha16(ppcTypeSwitch - address) \n" // load the address of the jump table for the switch + " la r30, lo16(ppcTypeSwitch - address)(r30) \n" + + " add r0, r30, r24 \n" // offset by our argument type + " mtctr r0 \n" // load the jump address into CTR + " bctr \n" // jump into the jump table/switch + " nop \n" + + // the jump table/switch based on the current argument type + "ppcTypeSwitch: \n" + " b ppcArgsEnd \n" + " b ppcArgIsInteger \n" + " b ppcArgIsFloat \n" + " b ppcArgIsDouble \n" + + // when we get here we have finished processing all the arguments + // everything is ready to go to call the function + "ppcArgsEnd: \n" + " mtctr r27 \n" // the function pointer is stored in r27, load that into CTR + " bctrl \n" // call the function. We have to do it this way so that the LR gets the proper + " nop \n" // return value (the next instruction below). So we have to branch from CTR instead of LR. + + // Restore registers and caller's stack frame, then return to caller + " lwz r1, 0(r1) \n" // restore the caller's stack pointer + " lwz r0, 8(r1) \n" // load in the caller's LR + " mtlr r0 \n" // restore the caller's LR + " lmw r23, -36(r1) \n" // restore registers r23 to r31 from the stack + " blr \n" // return back to the caller + " nop \n" + + // Integer argument (GPR register) + "ppcArgIsInteger: \n" + " addis r30, r31, ha16(ppcLoadIntReg - address) \n" // load the address to the jump table for integer registers + " la r30, lo16(ppcLoadIntReg - address)(r30) \n" + " mulli r0, r23, 8 \n" // each item in the jump table is 2 instructions (8 bytes) + " add r0, r0, r30 \n" // calculate ppcLoadIntReg[numUsedGPRRegs] + " lwz r30, 0(r29) \n" // load the next argument from the argument list into r30 + " cmpwi r23, 8 \n" // we can only load GPR3 through GPR10 (8 registers) + " bgt ppcLoadIntRegUpd \n" // if we're beyond 8 GPR registers, we're in the stack, go there + " mtctr r0 \n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers) + " bctr \n" // load the argument into a GPR register + " nop \n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadIntReg: \n" + " mr r3, r30 \n" // arg0 (to r3) + " b ppcLoadIntRegUpd \n" + " mr r4, r30 \n" // arg1 (to r4) + " b ppcLoadIntRegUpd \n" + " mr r5, r30 \n" // arg2 (to r5) + " b ppcLoadIntRegUpd \n" + " mr r6, r30 \n" // arg3 (to r6) + " b ppcLoadIntRegUpd \n" + " mr r7, r30 \n" // arg4 (to r7) + " b ppcLoadIntRegUpd \n" + " mr r8, r30 \n" // arg5 (to r8) + " b ppcLoadIntRegUpd \n" + " mr r9, r30 \n" // arg6 (to r9) + " b ppcLoadIntRegUpd \n" + " mr r10, r30 \n" // arg7 (to r10) + " b ppcLoadIntRegUpd \n" + // all GPR arguments still go on the stack + "ppcLoadIntRegUpd: \n" + " stw r30, 0(r26) \n" // store the argument into the next slot on the stack's argument list + " addi r23, r23, 1 \n" // count a used GPR register + " addi r29, r29, 4 \n" // move to the next argument on the list + " addi r26, r26, 4 \n" // adjust our argument stack pointer for the next + " b ppcNextArg \n" // next argument + + // single Float argument + "ppcArgIsFloat:\n" + " addis r30, r31, ha16(ppcLoadFloatReg - address) \n" // get the base address of the float register jump table + " la r30, lo16(ppcLoadFloatReg - address)(r30) \n" + " mulli r0, r28, 8 \n" // each jump table entry is 8 bytes + " add r0, r0, r30 \n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg] + " lfs f0, 0(r29) \n" // load the next argument as a float into f0 + " cmpwi r28, 13 \n" // can't load more than 13 float/double registers + " bgt ppcLoadFloatRegUpd \n" // if we're beyond 13 registers, just fall to inserting into the stack + " mtctr r0 \n" // jump into the float jump table + " bctr \n" + " nop \n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadFloatReg: \n" + " fmr f1, f0 \n" // arg0 (f1) + " b ppcLoadFloatRegUpd \n" + " fmr f2, f0 \n" // arg1 (f2) + " b ppcLoadFloatRegUpd \n" + " fmr f3, f0 \n" // arg2 (f3) + " b ppcLoadFloatRegUpd \n" + " fmr f4, f0 \n" // arg3 (f4) + " b ppcLoadFloatRegUpd \n" + " fmr f5, f0 \n" // arg4 (f5) + " b ppcLoadFloatRegUpd \n" + " fmr f6, f0 \n" // arg5 (f6) + " b ppcLoadFloatRegUpd \n" + " fmr f7, f0 \n" // arg6 (f7) + " b ppcLoadFloatRegUpd \n" + " fmr f8, f0 \n" // arg7 (f8) + " b ppcLoadFloatRegUpd \n" + " fmr f9, f0 \n" // arg8 (f9) + " b ppcLoadFloatRegUpd \n" + " fmr f10, f0 \n" // arg9 (f10) + " b ppcLoadFloatRegUpd \n" + " fmr f11, f0 \n" // arg10 (f11) + " b ppcLoadFloatRegUpd \n" + " fmr f12, f0 \n" // arg11 (f12) + " b ppcLoadFloatRegUpd \n" + " fmr f13, f0 \n" // arg12 (f13) + " b ppcLoadFloatRegUpd \n" + " nop \n" + // all float arguments still go on the stack + "ppcLoadFloatRegUpd: \n" + " stfs f0, 0(r26) \n" // store, as a single float, f0 (current argument) on to the stack argument list + " addi r23, r23, 1 \n" // a float register eats up a GPR register + " addi r28, r28, 1 \n" // ...and, of course, a float register + " addi r29, r29, 4 \n" // move to the next argument in the list + " addi r26, r26, 4 \n" // move to the next stack slot + " b ppcNextArg \n" // on to the next argument + " nop \n" + + // double Float argument + "ppcArgIsDouble: \n" + " addis r30, r31, ha16(ppcLoadDoubleReg - address) \n" // load the base address of the jump table for double registers + " la r30, lo16(ppcLoadDoubleReg - address)(r30) \n" + " mulli r0, r28, 8 \n" // each slot of the jump table is 8 bytes + " add r0, r0, r30 \n" // calculate ppcLoadDoubleReg[numUsedFloatReg] + " lfd f0, 0(r29) \n" // load the next argument, as a double float, into f0 + " cmpwi r28, 13 \n" // the first 13 floats must go into float registers also + " bgt ppcLoadDoubleRegUpd \n" // if we're beyond 13, then just put on to the stack + " mtctr r0 \n" // we're under 13, first load our register + " bctr \n" // jump into the jump table + " nop \n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadDoubleReg: \n" + " fmr f1, f0 \n" // arg0 (f1) + " b ppcLoadDoubleRegUpd \n" + " fmr f2, f0 \n" // arg1 (f2) + " b ppcLoadDoubleRegUpd \n" + " fmr f3, f0 \n" // arg2 (f3) + " b ppcLoadDoubleRegUpd \n" + " fmr f4, f0 \n" // arg3 (f4) + " b ppcLoadDoubleRegUpd \n" + " fmr f5, f0 \n" // arg4 (f5) + " b ppcLoadDoubleRegUpd \n" + " fmr f6, f0 \n" // arg5 (f6) + " b ppcLoadDoubleRegUpd \n" + " fmr f7, f0 \n" // arg6 (f7) + " b ppcLoadDoubleRegUpd \n" + " fmr f8, f0 \n" // arg7 (f8) + " b ppcLoadDoubleRegUpd \n" + " fmr f9, f0 \n" // arg8 (f9) + " b ppcLoadDoubleRegUpd \n" + " fmr f10, f0 \n" // arg9 (f10) + " b ppcLoadDoubleRegUpd \n" + " fmr f11, f0 \n" // arg10 (f11) + " b ppcLoadDoubleRegUpd \n" + " fmr f12, f0 \n" // arg11 (f12) + " b ppcLoadDoubleRegUpd \n" + " fmr f13, f0 \n" // arg12 (f13) + " b ppcLoadDoubleRegUpd \n" + " nop \n" + // all float arguments still go on the stack + "ppcLoadDoubleRegUpd: \n" + " stfd f0, 0(r26) \n" // store f0, as a double, into the argument list on the stack + " addi r23, r23, 2 \n" // a double float eats up two GPRs + " addi r28, r28, 1 \n" // ...and, of course, a float + " addi r29, r29, 8 \n" // increment to our next argument we need to process (8 bytes for the 64bit float) + " addi r26, r26, 8 \n" // increment to the next slot on the argument list on the stack (8 bytes) + " b ppcNextArg \n" // on to the next argument + " nop \n" +); + +asDWORD GetReturnedFloat() +{ + asDWORD f; + asm(" stfs f1, %0\n" : "=m"(f)); + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD f; + asm(" stfd f1, %0\n" : "=m"(f)); + return f; +} + +// puts the arguments in the correct place in the stack array. See comments above. +void stackArgs(const asDWORD *args, const asBYTE *argsType, int& numIntArgs, int& numFloatArgs, int& numDoubleArgs) +{ + int i; + int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2); + int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs; + + int typeIndex; + for( i = 0, typeIndex = 0; ; i++, typeIndex++ ) + { + // store the type + ppcArgsType[typeOffset++] = argsType[typeIndex]; + if( argsType[typeIndex] == ppcENDARG ) + break; + + switch( argsType[typeIndex] ) + { + case ppcFLOATARG: + // stow float + ppcArgs[argWordPos] = args[i]; // it's just a bit copy + numFloatArgs++; + argWordPos++; //add one word + break; + + case ppcDOUBLEARG: + // stow double + memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment + numDoubleArgs++; + argWordPos+=2; //add two words + i++;//doubles take up 2 argument slots + break; + + case ppcINTARG: + // stow register + ppcArgs[argWordPos] = args[i]; + numIntArgs++; + argWordPos++; + break; + } + } + + // close off the argument list (if we have max args we won't close it off until here) + ppcArgsType[typeOffset] = ppcENDARG; +} + +static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots + } + else + { + // no arguments, cap the type list + ppcArgsType[baseArgCount] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object (unless we are returning in memory) +static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory ) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // the first argument is the 'this' of the object + ppcArgs[baseArgCount] = (asDWORD)obj; + ppcArgsType[baseArgCount++] = ppcINTARG; + ppcArgsType[baseArgCount] = ppcENDARG; + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +// NOTE: on PPC the order for the args is reversed +static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + UNUSED_VAR(argSize); + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // stack any of the arguments + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + int numTotalArgs = intArgs + floatArgs + doubleArgs; + + // can we fit the object in at the end? + if( numTotalArgs < AS_PPC_MAX_ARGS ) + { + // put the object pointer at the end + int argPos = intArgs + floatArgs + (doubleArgs * 2); + ppcArgs[argPos] = (asDWORD)obj; + ppcArgsType[numTotalArgs++] = ppcINTARG; + ppcArgsType[numTotalArgs] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) +{ + // TODO: PPC does not yet support THISCALL_OBJFIRST/LAST + + // use a working array of types, we'll configure the final one in stackArgs + asBYTE argsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1]; + memset( argsType, 0, sizeof(argsType)); + + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *vftable = NULL; + int a, s; + + // convert the parameters that are < 4 bytes from little endian to big endian + int argDwordOffset = 0; + for( a = 0; a < (int)descr->parameterTypes.GetLength(); a++ ) + { + int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes(); + if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() ) + { + argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords(); + continue; + } + + // flip + asASSERT( numBytes == 1 || numBytes == 2 ); + switch( numBytes ) + { + case 1: + { + volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]); + asBYTE t = bPtr[0]; + bPtr[0] = bPtr[3]; + bPtr[3] = t; + t = bPtr[1]; + bPtr[1] = bPtr[2]; + bPtr[2] = t; + } + break; + case 2: + { + volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]); + asWORD t = wPtr[0]; + wPtr[0] = wPtr[1]; + wPtr[1] = t; + } + break; + } + argDwordOffset++; + } + + // mark all float/double/int arguments + if( !sysFunc->takesObjByVal ) + { + for( s = 0, a = 0; s < (int)descr->parameterTypes.GetLength(); s++, a++ ) + { + if( descr->parameterTypes[s].IsFloatType() && !descr->parameterTypes[s].IsReference() ) + { + argsType[a] = ppcFLOATARG; + } + else if( descr->parameterTypes[s].IsDoubleType() && !descr->parameterTypes[s].IsReference() ) + { + argsType[a] = ppcDOUBLEARG; + } + else + { + argsType[a] = ppcINTARG; + if( descr->parameterTypes[s].GetSizeOnStackDWords() == 2 ) + { + // Add an extra integer argument for the extra size + a++; + argsType[a] = ppcINTARG; + } + } + } + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + + int a = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + argsType[a++] = ppcINTARG; + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // TODO: Probably have to handle asOBJ_APP_FLOAT as a primitive + + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() ); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos) ); + spos++; + asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords(); + dpos += dwords; + paramSize += dwords; + for( asUINT i = 0; i < dwords; i++ ) + argsType[a++] = ppcINTARG; + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference() ) + argsType[a++] = ppcFLOATARG; + else if( descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference() ) + argsType[a++] = ppcDOUBLEARG; + else + argsType[a++] = ppcINTARG; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + { + paramBuffer[dpos++] = args[spos++]; + if( !descr->parameterTypes[n].IsDoubleType() ) // Double already knows it is 2 dwords + argsType[a++] = ppcINTARG; + } + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + int callConv = sysFunc->callConv; + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer ); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_PPC +#endif // AS_MAX_PORTABILITY + diff --git a/3rdparty/angelscript/src/as_callfunc_ppc_64.cpp b/3rdparty/angelscript/src/as_callfunc_ppc_64.cpp new file mode 100644 index 0000000..275f615 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_ppc_64.cpp @@ -0,0 +1,773 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_ppc_64.cpp +// +// These functions handle the actual calling of system functions +// +// This version is 64 bit PPC specific +// + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_PPC_64 +#if AS_PTR_SIZE == 2 +// TODO: Add support for PPC 64bit platforms with 64bit pointers, for example Linux PPC64 (big endian) and PPC64 (little endian) +#error This code has not been prepared for PPC with 64bit pointers. Most likely the ABI is different +#else + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#include +#include + +#ifdef __SNC__ +#include "ppu_asm_intrinsics.h" +#endif + + +BEGIN_AS_NAMESPACE + +// This part was written and tested by Jeff Slutter +// from Reactor Zero, Abril, 2007, for PlayStation 3, which +// is a PowerPC 64bit based architecture. Even though it is +// 64bit it seems the pointer size is still 32bit. + +// It still remains to be seen how well this code works +// on other PPC platforms, such as XBox 360, GameCube. + +#define AS_PPC_MAX_ARGS 32 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register type to load +// or zero if end of arguments +// The +1 is for when CallThis (object methods) is used +// Extra +1 when returning in memory +// Extra +1 in ppcArgsType to ensure zero end-of-args marker + +// TODO: multithread: The global variables must be removed to make the code thread safe + +extern "C" +{ + enum argTypes { ppcENDARG = 0, ppcINTARG = 1, ppcFLOATARG = 2, ppcDOUBLEARG = 3, ppcLONGARG = 4 }; + static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + 1 + 1 + 1]; + static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1]; +} + +// NOTE: these values are for PowerPC 64 bit. I'm sure things are different for PowerPC 32bit, but I don't have one. +// I'm pretty sure that PPC 32bit sets up a stack frame slightly different (only 24 bytes for linkage area for instance) +#define PPC_LINKAGE_SIZE (0x30) // how big the PPC linkage area is in a stack frame +#define PPC_NUM_REGSTORE (10) // how many registers of the PPC we need to store/restore for ppcFunc64() +#define PPC_REGSTORE_SIZE (8*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore +#define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame +#define PPC_STACK_SIZE(numParams) ( -(( ( (((numParams)<8)?8:(numParams))<<3) + EXTRA_STACK_SIZE + 15 ) & ~15) ) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes + +// This is PowerPC 64 bit specific +// Loads all data into the correct places and calls the function. +// ppcArgsType is an array containing a byte type (enum argTypes) for each argument. +// StackArgSizeInBytes is the size in bytes of the stack frame (takes into account linkage area, etc. must be multiple of 16) +extern "C" asQWORD ppcFunc64(const asDWORD* argsPtr, int StackArgSizeInBytes, asDWORD func); +asm("" + ".text\n" + ".align 4\n" + ".p2align 4,,15\n" + ".globl .ppcFunc64\n" + ".ppcFunc64:\n" + + // function prolog + "std %r22, -0x08(%r1)\n" // we need a register other than r0, to store the old stack pointer + "mr %r22, %r1\n" // store the old stack pointer, for now (to make storing registers easier) + "stdux %r1, %r1, %r4\n" // atomically store and update the stack pointer for the new stack frame (in case of a signal/interrupt) + "mflr %r0\n" // get the caller's LR register + "std %r0, 0x10(%r22)\n" // store the caller's LR register + "std %r23, -0x10(%r22)\n" // + "std %r24, -0x18(%r22)\n" // + "std %r25, -0x20(%r22)\n" // + "std %r26, -0x28(%r22)\n" // + "std %r27, -0x30(%r22)\n" // + "std %r28, -0x38(%r22)\n" // + "std %r29, -0x40(%r22)\n" // + "std %r30, -0x48(%r22)\n" // + "std %r31, -0x50(%r22)\n" // + "std %r3, 0x30(%r22)\n" // save our parameters + "std %r4, 0x38(%r22)\n" // + "std %r5, 0x40(%r22)\n" // + "mr %r31, %r1\n" // functions tend to store the stack pointer here too + + // initial registers for the function + "mr %r29, %r3\n" // (r29) args list + "lwz %r27, 0(%r5)\n" // load the function pointer to call. func actually holds the pointer to our function + "addi %r26, %r1, 0x30\n" // setup the pointer to the parameter area to the function we're going to call + "sub %r0,%r0,%r0\n" // zero out r0 + "mr %r23,%r0\n" // zero out r23, which holds the number of used GPR registers + "mr %r22,%r0\n" // zero our r22, which holds the number of used float registers + + // load the global ppcArgsType which holds the types of arguments for each argument + "lis %r25, ppcArgsType@ha\n" // load the upper 16 bits of the address to r25 + "addi %r25, %r25, ppcArgsType@l\n" // load the lower 16 bits of the address to r25 + "subi %r25, %r25, 1\n" // since we increment r25 on its use, we'll pre-decrement it + + // loop through the arguments + "ppcNextArg:\n" + "addi %r25, %r25, 1\n" // increment r25, our arg type pointer + // switch based on the current argument type (0:end, 1:int, 2:float 3:double) + "lbz %r24, 0(%r25)\n" // load the current argument type (it's a byte) + "mulli %r24, %r24, 4\n" // our jump table has 4 bytes per case (1 instruction) + "lis %r30, ppcTypeSwitch@ha\n" // load the address of the jump table for the switch + "addi %r30, %r30, ppcTypeSwitch@l\n" + "add %r0, %r30, %r24\n" // offset by our argument type + "mtctr %r0\n" // load the jump address into CTR + "bctr\n" // jump into the jump table/switch + "nop\n" + // the jump table/switch based on the current argument type + "ppcTypeSwitch:\n" + "b ppcArgsEnd\n" + "b ppcArgIsInteger\n" + "b ppcArgIsFloat\n" + "b ppcArgIsDouble\n" + "b ppcArgIsLong\n" + + // when we get here we have finished processing all the arguments + // everything is ready to go to call the function + "ppcArgsEnd:\n" + "mtctr %r27\n" // the function pointer is stored in r27, load that into CTR + "bctrl\n" // call the function. We have to do it this way so that the LR gets the proper + "nop\n" // return value (the next instruction below). So we have to branch from CTR instead of LR. + // when we get here, the function has returned, this is the function epilog + "ld %r11,0x00(%r1)\n" // load in the caller's stack pointer + "ld %r0,0x10(%r11)\n" // load in the caller's LR + "mtlr %r0\n" // restore the caller's LR + "ld %r22, -0x08(%r11)\n" // load registers + "ld %r23, -0x10(%r11)\n" // + "ld %r24, -0x18(%r11)\n" // + "ld %r25, -0x20(%r11)\n" // + "ld %r26, -0x28(%r11)\n" // + "ld %r27, -0x30(%r11)\n" // + "ld %r28, -0x38(%r11)\n" // + "ld %r29, -0x40(%r11)\n" // + "ld %r30, -0x48(%r11)\n" // + "ld %r31, -0x50(%r11)\n" // + "mr %r1, %r11\n" // restore the caller's SP + "blr\n" // return back to the caller + "nop\n" + // Integer argument (GPR register) + "ppcArgIsInteger:\n" + "lis %r30,ppcLoadIntReg@ha\n" // load the address to the jump table for integer registers + "addi %r30, %r30, ppcLoadIntReg@l\n" + "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes) + "add %r0, %r0, %r30\n" // calculate ppcLoadIntReg[numUsedGPRRegs] + "lwz %r30,0(%r29)\n" // load the next argument from the argument list into r30 + "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers) + "bgt ppcLoadIntRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there + "mtctr %r0\n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers) + "bctr\n" // load the argument into a GPR register + "nop\n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadIntReg:\n" + "mr %r3,%r30\n" // arg0 (to r3) + "b ppcLoadIntRegUpd\n" + "mr %r4,%r30\n" // arg1 (to r4) + "b ppcLoadIntRegUpd\n" + "mr %r5,%r30\n" // arg2 (to r5) + "b ppcLoadIntRegUpd\n" + "mr %r6,%r30\n" // arg3 (to r6) + "b ppcLoadIntRegUpd\n" + "mr %r7,%r30\n" // arg4 (to r7) + "b ppcLoadIntRegUpd\n" + "mr %r8,%r30\n" // arg5 (to r8) + "b ppcLoadIntRegUpd\n" + "mr %r9,%r30\n" // arg6 (to r9) + "b ppcLoadIntRegUpd\n" + "mr %r10,%r30\n" // arg7 (to r10) + "b ppcLoadIntRegUpd\n" + + // all GPR arguments still go on the stack + "ppcLoadIntRegUpd:\n" + "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list + "addi %r23, %r23, 1\n" // count a used GPR register + "addi %r29, %r29, 4\n" // move to the next argument on the list + "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next + "b ppcNextArg\n" // next argument + + // single Float argument + "ppcArgIsFloat:\n" + "lis %r30,ppcLoadFloatReg@ha\n" // get the base address of the float register jump table + "addi %r30, %r30, ppcLoadFloatReg@l\n" + "mulli %r0, %r22 ,8\n" // each jump table entry is 8 bytes + "add %r0, %r0, %r30\n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg] + "lfs 0, 0(%r29)\n" // load the next argument as a float into f0 + "cmpwi %r22, 13\n" // can't load more than 13 float/double registers + "bgt ppcLoadFloatRegUpd\n" // if we're beyond 13 registers, just fall to inserting into the stack + "mtctr %r0\n" // jump into the float jump table + "bctr\n" + "nop\n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadFloatReg:\n" + "fmr 1,0\n" // arg0 (f1) + "b ppcLoadFloatRegUpd\n" + "fmr 2,0\n" // arg1 (f2) + "b ppcLoadFloatRegUpd\n" + "fmr 3,0\n" // arg2 (f3) + "b ppcLoadFloatRegUpd\n" + "fmr 4,0\n" // arg3 (f4) + "b ppcLoadFloatRegUpd\n" + "fmr 5,0\n" // arg4 (f5) + "b ppcLoadFloatRegUpd\n" + "fmr 6,0\n" // arg5 (f6) + "b ppcLoadFloatRegUpd\n" + "fmr 7,0\n" // arg6 (f7) + "b ppcLoadFloatRegUpd\n" + "fmr 8,0\n" // arg7 (f8) + "b ppcLoadFloatRegUpd\n" + "fmr 9,0\n" // arg8 (f9) + "b ppcLoadFloatRegUpd\n" + "fmr 10,0\n" // arg9 (f10) + "b ppcLoadFloatRegUpd\n" + "fmr 11,0\n" // arg10 (f11) + "b ppcLoadFloatRegUpd\n" + "fmr 12,0\n" // arg11 (f12) + "b ppcLoadFloatRegUpd\n" + "fmr 13,0\n" // arg12 (f13) + "b ppcLoadFloatRegUpd\n" + "nop\n" + // all float arguments still go on the stack + "ppcLoadFloatRegUpd:\n" + "stfs 0, 0x04(%r26)\n" // store, as a single float, f0 (current argument) on to the stack argument list + "addi %r23, %r23, 1\n" // a float register eats up a GPR register + "addi %r22, %r22, 1\n" // ...and, of course, a float register + "addi %r29, %r29, 4\n" // move to the next argument in the list + "addi %r26, %r26, 8\n" // move to the next stack slot + "b ppcNextArg\n" // on to the next argument + "nop\n" + // double Float argument + "ppcArgIsDouble:\n" + "lis %r30, ppcLoadDoubleReg@ha\n" // load the base address of the jump table for double registers + "addi %r30, %r30, ppcLoadDoubleReg@l\n" + "mulli %r0, %r22, 8\n" // each slot of the jump table is 8 bytes + "add %r0, %r0, %r30\n" // calculate ppcLoadDoubleReg[numUsedFloatReg] + "lfd 0, 0(%r29)\n" // load the next argument, as a double float, into f0 + "cmpwi %r22,13\n" // the first 13 floats must go into float registers also + "bgt ppcLoadDoubleRegUpd\n" // if we're beyond 13, then just put on to the stack + "mtctr %r0\n" // we're under 13, first load our register + "bctr\n" // jump into the jump table + "nop\n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadDoubleReg:\n" + "fmr 1,0\n" // arg0 (f1) + "b ppcLoadDoubleRegUpd\n" + "fmr 2,0\n" // arg1 (f2) + "b ppcLoadDoubleRegUpd\n" + "fmr 3,0\n" // arg2 (f3) + "b ppcLoadDoubleRegUpd\n" + "fmr 4,0\n" // arg3 (f4) + "b ppcLoadDoubleRegUpd\n" + "fmr 5,0\n" // arg4 (f5) + "b ppcLoadDoubleRegUpd\n" + "fmr 6,0\n" // arg5 (f6) + "b ppcLoadDoubleRegUpd\n" + "fmr 7,0\n" // arg6 (f7) + "b ppcLoadDoubleRegUpd\n" + "fmr 8,0\n" // arg7 (f8) + "b ppcLoadDoubleRegUpd\n" + "fmr 9,0\n" // arg8 (f9) + "b ppcLoadDoubleRegUpd\n" + "fmr 10,0\n" // arg9 (f10) + "b ppcLoadDoubleRegUpd\n" + "fmr 11,0\n" // arg10 (f11) + "b ppcLoadDoubleRegUpd\n" + "fmr 12,0\n" // arg11 (f12) + "b ppcLoadDoubleRegUpd\n" + "fmr 13,0\n" // arg12 (f13) + "b ppcLoadDoubleRegUpd\n" + "nop\n" + // all float arguments still go on the stack + "ppcLoadDoubleRegUpd:\n" + "stfd 0,0(%r26)\n" // store f0, as a double, into the argument list on the stack + "addi %r23, %r23, 1\n" // a double float eats up one GPR + "addi %r22, %r22, 1\n" // ...and, of course, a float + "addi %r29, %r29, 8\n" // increment to our next argument we need to process (8 bytes for the 64bit float) + "addi %r26, %r26, 8\n" // increment to the next slot on the argument list on the stack (8 bytes) + "b ppcNextArg\n" // on to the next argument + "nop\n" + + // Long (64 bit int) argument + "ppcArgIsLong:\n" + "lis %r30,ppcLoadLongReg@ha\n" // load the address to the jump table for integer64 + "addi %r30, %r30, ppcLoadLongReg@l\n" + "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes) + "add %r0, %r0, %r30\n" // calculate ppcLoadLongReg[numUsedGPRRegs] + "ld %r30,0(%r29)\n" // load the next argument from the argument list into r30 + "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers) + "bgt ppcLoadLongRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there + "mtctr %r0\n" // load the address of our ppcLoadLongReg jump table (we're below 8 GPR registers) + "bctr\n" // load the argument into a GPR register + "nop\n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadLongReg:\n" + "mr %r3,%r30\n" // arg0 (to r3) + "b ppcLoadLongRegUpd\n" + "mr %r4,%r30\n" // arg1 (to r4) + "b ppcLoadLongRegUpd\n" + "mr %r5,%r30\n" // arg2 (to r5) + "b ppcLoadLongRegUpd\n" + "mr %r6,%r30\n" // arg3 (to r6) + "b ppcLoadLongRegUpd\n" + "mr %r7,%r30\n" // arg4 (to r7) + "b ppcLoadLongRegUpd\n" + "mr %r8,%r30\n" // arg5 (to r8) + "b ppcLoadLongRegUpd\n" + "mr %r9,%r30\n" // arg6 (to r9) + "b ppcLoadLongRegUpd\n" + "mr %r10,%r30\n" // arg7 (to r10) + "b ppcLoadLongRegUpd\n" + + // all GPR arguments still go on the stack + "ppcLoadLongRegUpd:\n" + "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list + "addi %r23, %r23, 1\n" // count a used GPR register + "addi %r29, %r29, 8\n" // move to the next argument on the list + "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next + "b ppcNextArg\n" // next argument +); + +static asDWORD GetReturnedFloat(void) +{ + asDWORD f; +#ifdef __SNC__ + __stfs( __freg(1), 0, (void*)&f); +#else + asm(" stfs 1, %0\n" : "=m"(f)); +#endif + return f; +} + +static asQWORD GetReturnedDouble(void) +{ + asQWORD f; +#ifdef __SNC__ + __stfd( __freg(1), 0, (void*)&f); +#else + asm(" stfd 1, %0\n" : "=m"(f)); +#endif + return f; +} + +// puts the arguments in the correct place in the stack array. See comments above. +static void stackArgs( const asDWORD *args, const asBYTE *argsType, int &numIntArgs, int &numFloatArgs, int &numDoubleArgs, int &numLongArgs ) +{ + // initialize our offset based on any already placed arguments + int i; + int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2) + (numLongArgs*2); + int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs + numLongArgs; + + int typeIndex; + for( i = 0, typeIndex = 0; ; i++, typeIndex++ ) + { + // store the type + ppcArgsType[typeOffset++] = argsType[typeIndex]; + if( argsType[typeIndex] == ppcENDARG ) + break; + + switch( argsType[typeIndex] ) + { + case ppcFLOATARG: + { + // stow float + ppcArgs[argWordPos] = args[i]; // it's just a bit copy + numFloatArgs++; + argWordPos++; //add one word + } + break; + + case ppcDOUBLEARG: + { + // stow double + memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment + numDoubleArgs++; + argWordPos+=2; //add two words + i++;//doubles take up 2 argument slots + } + break; + + case ppcINTARG: + { + // stow register + ppcArgs[argWordPos] = args[i]; + numIntArgs++; + argWordPos++; + } + break; + + case ppcLONGARG: + { + // stow long + memcpy( &ppcArgs[argWordPos], &args[i], 8 ); // for alignment purposes, we use memcpy + numLongArgs++; + argWordPos += 2; // add two words + i++; // longs take up 2 argument slots + } + break; + } + } + + // close off the argument list (if we have max args we won't close it off until here) + ppcArgsType[typeOffset] = ppcENDARG; +} + +static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs; + } + else + { + // no arguments, cap the type list + ppcArgsType[baseArgCount] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object (unless we are returning in memory) +static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory ) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // the first argument is the 'this' of the object + ppcArgs[baseArgCount] = (asDWORD)obj; + ppcArgsType[baseArgCount++] = ppcINTARG; + ppcArgsType[baseArgCount] = ppcENDARG; + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +// NOTE: on PPC the order for the args is reversed +static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + UNUSED_VAR(argSize); + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // stack any of the arguments + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + int numTotalArgs = intArgs + floatArgs + doubleArgs; + + // can we fit the object in at the end? + if( numTotalArgs < AS_PPC_MAX_ARGS ) + { + // put the object pointer at the end + int argPos = intArgs + floatArgs + (doubleArgs * 2) + (longArgs *2); + ppcArgs[argPos] = (asDWORD)obj; + ppcArgsType[numTotalArgs++] = ppcINTARG; + ppcArgsType[numTotalArgs] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// returns true if the given parameter is a 'variable argument' +inline bool IsVariableArgument( asCDataType type ) +{ + return (type.GetTokenType() == ttQuestion) ? true : false; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) +{ + // TODO: PPC 64 does not yet support THISCALL_OBJFIRST/LAST + + // use a working array of types, we'll configure the final one in stackArgs + asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1]; + memset( argsType, 0, sizeof(argsType)); + + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *vftable = NULL; + int a; + + // convert the parameters that are < 4 bytes from little endian to big endian + int argDwordOffset = 0; + int totalArgumentCount = 0; + + for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a ) + { + // get the size for the parameter + int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes(); + ++totalArgumentCount; + + // is this a variable argument? + // for variable arguments, the typeID will always follow...but we know it is 4 bytes + // so we can skip that parameter automatically. + bool isVarArg = IsVariableArgument( descr->parameterTypes[a] ); + if( isVarArg ) + { + ++totalArgumentCount; + } + + if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() ) + { + // DWORD or larger parameter --- no flipping needed + argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords(); + } + else + { + // flip + asASSERT( numBytes == 1 || numBytes == 2 ); + switch( numBytes ) + { + case 1: + { + volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]); + asBYTE t = bPtr[0]; + bPtr[0] = bPtr[3]; + bPtr[3] = t; + t = bPtr[1]; + bPtr[1] = bPtr[2]; + bPtr[2] = t; + } + break; + case 2: + { + volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]); + asWORD t = wPtr[0]; + wPtr[0] = wPtr[1]; + wPtr[1] = t; + } + break; + } + ++argDwordOffset; + } + + if( isVarArg ) + { + // skip the implicit typeID + ++argDwordOffset; + } + } + + asASSERT( totalArgumentCount <= AS_PPC_MAX_ARGS ); + + // mark all float/double/int arguments + int argIndex = 0; + for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a, ++argIndex ) + { + // get the base type + argsType[argIndex] = ppcINTARG; + if( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcFLOATARG; + } + if( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcDOUBLEARG; + } + if( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcLONGARG; + } + + // if it is a variable argument, account for the typeID + if( IsVariableArgument(descr->parameterTypes[a]) ) + { + // implicitly add another parameter (AFTER the parameter above), for the TypeID + argsType[++argIndex] = ppcINTARG; + } + } + asASSERT( argIndex == totalArgumentCount ); + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !(descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_ARRAY) ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + ++paramSize; + } + else +#endif + { + // NOTE: we may have to do endian flipping here + + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() ); + + // Delete the original memory + engine->CallFree( *(char**)(args+spos) ); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + { + paramBuffer[dpos++] = args[spos++]; + } + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + // if this was a variable argument parameter, then account for the implicit typeID + if( IsVariableArgument( descr->parameterTypes[n] ) ) + { + // the TypeID is just a DWORD + paramBuffer[dpos++] = args[spos++]; + ++paramSize; + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + // one last verification to make sure things are how we expect + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer ); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer ); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + if( sysFunc->hostReturnFloat ) + { + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + else if( sysFunc->hostReturnSize == 1 ) + { + // Move the bits to the higher value to compensate for the adjustment that the caller does + retQW <<= 32; + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_PTR_SIZE == 2 +#endif // AS_PPC_64 +#endif // AS_MAX_PORTABILITY + diff --git a/3rdparty/angelscript/src/as_callfunc_sh4.cpp b/3rdparty/angelscript/src/as_callfunc_sh4.cpp new file mode 100644 index 0000000..b24be9a --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_sh4.cpp @@ -0,0 +1,393 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_sh4.cpp +// +// These functions handle the actual calling of system functions +// +// This version is SH4 specific and was originally written +// by Fredrik Ehnbom in May, 2004 +// Later updated for angelscript 2.0.0 by Fredrik Ehnbom in Jan, 2005 + +// References: +// * http://www.renesas.com/avs/resource/japan/eng/pdf/mpumcu/e602156_sh4.pdf +// * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcechp40/html/_callsh4_SH_4_Calling_Standard.asp + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_SH4 + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#include +#include + +BEGIN_AS_NAMESPACE + +#define AS_SH4_MAX_ARGS 32 +// The array used to send values to the correct places. +// first 0-4 regular values to load into the r4-r7 registers +// then 0-8 float values to load into the fr4-fr11 registers +// then (AS_SH4_MAX_ARGS - 12) values to load onto the stack +// the +1 is for when CallThis (object methods) is used +// extra +1 when returning in memory +extern "C" { +static asDWORD sh4Args[AS_SH4_MAX_ARGS + 1 + 1]; +} + +// Loads all data into the correct places and calls the function. +// intArgSize is the size in bytes for how much data to put in int registers +// floatArgSize is the size in bytes for how much data to put in float registers +// stackArgSize is the size in bytes for how much data to put on the callstack +extern "C" asQWORD sh4Func(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func); + +asm("" +" .align 4\n" +" .global _sh4Func\n" +"_sh4Func:\n" +" mov.l r14,@-r15\n" +" mov.l r13,@-r15\n" +" mov.l r12,@-r15\n" +" sts.l pr,@-r15\n" // must be saved since we call a subroutine +" mov r7, r14\n" // func +" mov r6, r13\n" // stackArgSize +" mov.l r5,@-r15\n" // floatArgSize +" mov.l sh4Args,r0\n" +" pref @r0\n" +" mov r4, r1\n" // intArgsize +" mov #33*4,r2\n" +" extu.b r2,r2\n" // make unsigned (33*4 = 132 => 128) +" mov.l @(r0,r2), r2\n" // r2 has adress for when returning in memory +"_sh4f_intarguments:\n" // copy all the int arguments to the respective registers +" mov #4*2*2,r3\n" // calculate how many bytes to skip +" sub r1,r3\n" +" braf r3\n" +" add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot) +" mov.l @(r0,r1),r7\n" // 4 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r6\n" // 3 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r5\n" // 2 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r4\n" // 1 argument +" nop\n" +"_sh4f_floatarguments:\n" // copy all the float arguments to the respective registers +" add #4*4, r0\n" +" mov.l @r15+,r1\n" // floatArgSize +" mov #8*2*2,r3\n" // calculate how many bytes to skip +" sub r1,r3\n" +" braf r3\n" +" add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot) +" fmov.s @(r0,r1),fr11\n" // 8 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr10\n" // 7 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr9\n" // 6 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr8\n" // 5 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr7\n" // 4 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr6\n" // 3 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr5\n" // 2 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr4\n" // 1 argument +" nop\n" +"_sh4f_stackarguments:\n" // copy all the stack argument onto the stack +" add #8*4, r0\n" +" mov r0, r1\n" +" mov #0, r0\n" // init position counter (also used as a 0-check on the line after) +" cmp/eq r0, r13\n" +" bt _sh4f_functioncall\n" // no arguments to push onto the stack +" mov r13, r3\n" // stackArgSize +" sub r3,r15\n" // "allocate" space on the stack +" shlr2 r3\n" // make into a counter +"_sh4f_stackloop:\n" +" mov.l @r1+, r12\n" +" mov.l r12, @(r0, r15)\n" +" add #4, r0\n" +" dt r3\n" +" bf _sh4f_stackloop\n" +"_sh4f_functioncall:\n" +" jsr @r14\n" // no arguments +" nop\n" +" add r13, r15\n" // restore stack position +" lds.l @r15+,pr\n" +" mov.l @r15+, r12\n" +" mov.l @r15+, r13\n" +" rts\n" +" mov.l @r15+, r14\n" // delayed slot +"\n" +" .align 4\n" +"sh4Args:\n" +" .long _sh4Args\n" +); + +// puts the arguments in the correct place in the sh4Args-array. See comments above. +// This could be done better. +inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) { + int i; + + int argBit = 1; + for (i = 0; i < argNum; i++) { + if (hostFlags & argBit) { + if (numRegFloatArgs < 12 - 4) { + // put in float register + sh4Args[4 + numRegFloatArgs] = args[i]; + numRegFloatArgs++; + } else { + // put in stack + sh4Args[4 + 8 + numRestArgs] = args[i]; + numRestArgs++; + } + } else { + if (numRegIntArgs < 8 - 4) { + // put in int register + sh4Args[numRegIntArgs] = args[i]; + numRegIntArgs++; + } else { + // put in stack + sh4Args[4 + 8 + numRestArgs] = args[i]; + numRestArgs++; + } + } + argBit <<= 1; + } +} +asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the sh4Args array + if (argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object +asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 1; + int floatArgs = 0; + int restArgs = 0; + + sh4Args[0] = (asDWORD) obj; + + // put the arguments in the correct places in the sh4Args array + if (argNum >= 1) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + + // put the arguments in the correct places in the sh4Args array + if (argNum >= 1) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + if (intArgs < 4) { + sh4Args[intArgs] = (asDWORD) obj; + intArgs++; + } else { + sh4Args[4 + 8 + restArgs] = (asDWORD) obj; + restArgs++; + } + + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("fmov.s fr0, %0\n" : "=m"(f)); + + return f; +} + +// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4 +// so this isn't really used... +asQWORD GetReturnedDouble() +{ + asQWORD d; + + asm("fmov dr0, %0\n" : "=m"(d)); + + return d; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) +{ + // TODO: SH4 does not yet support THISCALL_OBJFIRST/LAST + + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *vftable; + + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + sh4Args[AS_SH4_MAX_ARGS+1] = (asDWORD) retPointer; + } + + asASSERT(descr->parameterTypes.GetLength() <= 32); + + // mark all float arguments + int argBit = 1; + int hostFlags = 0; + int intArgs = 0; + for( asUINT a = 0; a < descr->parameterTypes.GetLength(); a++ ) { + if (descr->parameterTypes[a].IsFloatType()) { + hostFlags |= argBit; + } else intArgs++; + argBit <<= 1; + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_SH4 +#endif // AS_MAX_PORTABILITY + + diff --git a/3rdparty/angelscript/src/as_callfunc_x64_gcc.cpp b/3rdparty/angelscript/src/as_callfunc_x64_gcc.cpp new file mode 100644 index 0000000..b557395 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_x64_gcc.cpp @@ -0,0 +1,477 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +/* + * Implements the AMD64 calling convention for gcc-based 64bit Unices + * + * Author: Ionut "gargltk" Leonte + * + * Initial author: niteice + * + * Added support for functor methods by Jordi Oliveras Rovira in April, 2014. + */ + +// Useful references for the System V AMD64 ABI: +// http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ +// http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_amd64.pdf + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X64_GCC + +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_context.h" + +BEGIN_AS_NAMESPACE + +enum argTypes { x64INTARG = 0, x64FLOATARG = 1 }; +typedef asQWORD ( *funcptr_t )( void ); + +#define X64_MAX_ARGS 32 +#define MAX_CALL_INT_REGISTERS 6 +#define MAX_CALL_SSE_REGISTERS 8 +#define X64_CALLSTACK_SIZE ( X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 ) + +// Note to self: Always remember to inform the used registers on the clobber line, +// so that the gcc optimizer doesn't try to use them for other things + +static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, int cnt, funcptr_t func, asQWORD &retQW2, bool returnFloat) +{ + // Need to flag the variable as volatile so the compiler doesn't optimize out the variable + volatile asQWORD retQW1 = 0; + + // Reference: http://www.x86-64.org/documentation/abi.pdf + + __asm__ __volatile__ ( + + " movq %0, %%rcx \n" // rcx = cnt + " movq %1, %%r10 \n" // r10 = args + " movq %2, %%r11 \n" // r11 = func + + // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls + " movq %%rsp, %%r15 \n" +#ifdef __OPTIMIZE__ + // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15 + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code + " .cfi_def_cfa_register r15 \n" +#endif + + // Skip the first 128 bytes on the stack frame, called "red zone", + // that might be used by the compiler to store temporary values + " sub $128, %%rsp \n" + + // Make sure the stack pointer will be aligned to 16 bytes when the function is called + " movq %%rcx, %%rdx \n" + " salq $3, %%rdx \n" + " movq %%rsp, %%rax \n" + " sub %%rdx, %%rax \n" + " and $15, %%rax \n" + " sub %%rax, %%rsp \n" + + // Push the stack parameters, i.e. the arguments that won't be loaded into registers + " movq %%rcx, %%rsi \n" + " testl %%esi, %%esi \n" + " jle endstack \n" + " subl $1, %%esi \n" + " xorl %%edx, %%edx \n" + " leaq 8(, %%rsi, 8), %%rcx \n" + "loopstack: \n" + " movq 112(%%r10, %%rdx), %%rax \n" + " pushq %%rax \n" + " addq $8, %%rdx \n" + " cmpq %%rcx, %%rdx \n" + " jne loopstack \n" + "endstack: \n" + + // Populate integer and floating point parameters + " movq %%r10, %%rax \n" + " mov (%%rax), %%rdi \n" + " mov 8(%%rax), %%rsi \n" + " mov 16(%%rax), %%rdx \n" + " mov 24(%%rax), %%rcx \n" + " mov 32(%%rax), %%r8 \n" + " mov 40(%%rax), %%r9 \n" + " add $48, %%rax \n" + " movsd (%%rax), %%xmm0 \n" + " movsd 8(%%rax), %%xmm1 \n" + " movsd 16(%%rax), %%xmm2 \n" + " movsd 24(%%rax), %%xmm3 \n" + " movsd 32(%%rax), %%xmm4 \n" + " movsd 40(%%rax), %%xmm5 \n" + " movsd 48(%%rax), %%xmm6 \n" + " movsd 56(%%rax), %%xmm7 \n" + + // Call the function + " call *%%r11 \n" + + // Restore stack pointer + " mov %%r15, %%rsp \n" +#ifdef __OPTIMIZE__ + // Inform the stack unwind logic that the stack pointer has been restored + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code + " .cfi_def_cfa_register rsp \n" +#endif + + // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value + " movl %5, %%ecx \n" + " testb %%cl, %%cl \n" + " je intret \n" + " lea %3, %%rax \n" + " movq %%xmm0, (%%rax) \n" + " lea %4, %%rdx \n" + " movq %%xmm1, (%%rdx) \n" + " jmp endcall \n" + "intret: \n" + " movq %%rax, %3 \n" + " movq %%rdx, %4 \n" + "endcall: \n" + + : : "r" ((asQWORD)cnt), "r" (args), "r" (func), "m" (retQW1), "m" (retQW2), "m" (returnFloat) + : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", + "%rdi", "%rsi", "%rax", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11", "%r15"); + + return retQW1; +} + +// returns true if the given parameter is a 'variable argument' +static inline bool IsVariableArgument( asCDataType type ) +{ + return ( type.GetTokenType() == ttQuestion ) ? true : false; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + asQWORD retQW = 0; + asDWORD *stack_pointer = args; + funcptr_t *vftable = NULL; + int totalArgumentCount = 0; + int n = 0; + int param_post = 0; + int argIndex = 0; + funcptr_t func = (funcptr_t)sysFunc->func; + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + // Determine the real function pointer in case of virtual method + if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) ) +#else + if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) +#endif + { + vftable = *((funcptr_t**)obj); + func = vftable[FuncPtrToUInt(asFUNCTION_t(func)) >> 3]; + } + + // Determine the type of the arguments, and prepare the input array for the X64_CallFunction + asQWORD paramBuffer[X64_CALLSTACK_SIZE] = { 0 }; + asBYTE argsType[X64_CALLSTACK_SIZE] = { 0 }; + + switch ( callConv ) + { + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL_RETURNINMEM: + { + paramBuffer[0] = (asPWORD)retPointer; + argsType[0] = x64INTARG; + + argIndex = 1; + + break; + } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJLAST: + case ICC_VIRTUAL_THISCALL_OBJLAST: + param_post = 2; +#endif + case ICC_THISCALL: + case ICC_VIRTUAL_THISCALL: + case ICC_CDECL_OBJFIRST: + { + paramBuffer[0] = (asPWORD)obj; + argsType[0] = x64INTARG; + + argIndex = 1; + + break; + } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJLAST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + param_post = 2; +#endif + case ICC_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + { + paramBuffer[0] = (asPWORD)retPointer; + paramBuffer[1] = (asPWORD)obj; + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + + argIndex = 2; + + break; + } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + { + paramBuffer[0] = (asPWORD)obj; + paramBuffer[1] = (asPWORD)secondObject; + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + + argIndex = 2; + break; + } + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + { + paramBuffer[0] = (asPWORD)retPointer; + paramBuffer[1] = (asPWORD)obj; + paramBuffer[2] = (asPWORD)secondObject; + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + argsType[2] = x64INTARG; + + argIndex = 3; + break; + } +#endif + case ICC_CDECL_OBJLAST: + param_post = 1; + break; + case ICC_CDECL_OBJLAST_RETURNINMEM: + { + paramBuffer[0] = (asPWORD)retPointer; + argsType[0] = x64INTARG; + + argIndex = 1; + param_post = 1; + + break; + } + } + + int argumentCount = ( int )descr->parameterTypes.GetLength(); + for( int a = 0; a < argumentCount; ++a ) + { + const asCDataType &parmType = descr->parameterTypes[a]; + if( parmType.IsFloatType() && !parmType.IsReference() ) + { + argsType[argIndex] = x64FLOATARG; + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(float)); + argIndex++; + stack_pointer++; + } + else if( parmType.IsDoubleType() && !parmType.IsReference() ) + { + argsType[argIndex] = x64FLOATARG; + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(double)); + argIndex++; + stack_pointer += 2; + } + else if( IsVariableArgument( parmType ) ) + { + // The variable args are really two, one pointer and one type id + argsType[argIndex] = x64INTARG; + argsType[argIndex+1] = x64INTARG; + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(void*)); + memcpy(paramBuffer + argIndex + 1, stack_pointer + 2, sizeof(asDWORD)); + argIndex += 2; + stack_pointer += 3; + } + else if( parmType.IsPrimitive() || + parmType.IsReference() || + parmType.IsObjectHandle() ) + { + argsType[argIndex] = x64INTARG; + if( parmType.GetSizeOnStackDWords() == 1 ) + { + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asDWORD)); + stack_pointer++; + } + else + { + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD)); + stack_pointer += 2; + } + argIndex++; + } + else + { + // An object is being passed by value + if( (parmType.GetTypeInfo()->flags & COMPLEX_MASK) || + parmType.GetSizeInMemoryDWords() > 4 ) + { + // Copy the address of the object + argsType[argIndex] = x64INTARG; + memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD)); + argIndex++; + } + else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLINTS) || + (parmType.GetTypeInfo()->flags & asOBJ_APP_PRIMITIVE) ) + { + // Copy the value of the object + if( parmType.GetSizeInMemoryDWords() > 2 ) + { + argsType[argIndex] = x64INTARG; + argsType[argIndex+1] = x64INTARG; + memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes()); + argIndex += 2; + } + else + { + argsType[argIndex] = x64INTARG; + memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes()); + argIndex++; + } + // Delete the original memory + engine->CallFree(*(void**)stack_pointer); + } + else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) || + (parmType.GetTypeInfo()->flags & asOBJ_APP_FLOAT) ) + { + // Copy the value of the object + if( parmType.GetSizeInMemoryDWords() > 2 ) + { + argsType[argIndex] = x64FLOATARG; + argsType[argIndex+1] = x64FLOATARG; + memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes()); + argIndex += 2; + } + else + { + argsType[argIndex] = x64FLOATARG; + memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes()); + argIndex++; + } + // Delete the original memory + engine->CallFree(*(void**)stack_pointer); + } + stack_pointer += 2; + } + } + + // For the CDECL_OBJ_LAST calling convention we need to add the object pointer as the last argument + if( param_post ) + { +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + paramBuffer[argIndex] = (asPWORD)obj; +#else + paramBuffer[argIndex] = (asPWORD)(param_post > 1 ? secondObject : obj); +#endif + argsType[argIndex] = x64INTARG; + argIndex++; + } + + totalArgumentCount = argIndex; + + /* + * Q: WTF is going on here !? + * + * A: The idea is to pre-arange the parameters so that X64_CallFunction() can do + * it's little magic which must work regardless of how the compiler decides to + * allocate registers. Basically: + * - the first MAX_CALL_INT_REGISTERS entries in tempBuff will + * contain the values/types of the x64INTARG parameters - that is the ones who + * go into the registers. If the function has less then MAX_CALL_INT_REGISTERS + * integer parameters then the last entries will be set to 0 + * - the next MAX_CALL_SSE_REGISTERS entries will contain the float/double arguments + * that go into the floating point registers. If the function has less than + * MAX_CALL_SSE_REGISTERS floating point parameters then the last entries will + * be set to 0 + * - index MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS marks the start of the + * parameters which will get passed on the stack. These are added to the array + * in reverse order so that X64_CallFunction() can simply push them to the stack + * without the need to perform further tests + */ + asQWORD tempBuff[X64_CALLSTACK_SIZE] = { 0 }; + asBYTE argsSet[X64_CALLSTACK_SIZE] = { 0 }; + int used_int_regs = 0; + int used_sse_regs = 0; + int used_stack_args = 0; + int idx = 0; + for ( n = 0; ( n < totalArgumentCount ) && ( used_int_regs < MAX_CALL_INT_REGISTERS ); n++ ) + { + if ( argsType[n] == x64INTARG ) + { + argsSet[n] = 1; + tempBuff[idx++] = paramBuffer[n]; + used_int_regs++; + } + } + idx = MAX_CALL_INT_REGISTERS; + for ( n = 0; ( n < totalArgumentCount ) && ( used_sse_regs < MAX_CALL_SSE_REGISTERS ); n++ ) + { + if ( argsType[n] == x64FLOATARG ) + { + argsSet[n] = 1; + tempBuff[idx++] = paramBuffer[n]; + used_sse_regs++; + } + } + idx = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS; + for ( n = totalArgumentCount - 1; n >= 0; n-- ) + { + if ( !argsSet[n] ) + { + tempBuff[idx++] = paramBuffer[n]; + used_stack_args++; + } + } + + retQW = X64_CallFunction( tempBuff, used_stack_args, func, retQW2, sysFunc->hostReturnFloat ); + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_X64_GCC +#endif // AS_MAX_PORTABILITY + diff --git a/3rdparty/angelscript/src/as_callfunc_x64_mingw.cpp b/3rdparty/angelscript/src/as_callfunc_x64_mingw.cpp new file mode 100644 index 0000000..1c5bdb5 --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_x64_mingw.cpp @@ -0,0 +1,348 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// +// This code was adapted from as_callfunc_x64_msvc by _Vicious_ on August 20th, 2011. +// +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. +// + +#include + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X64_MINGW + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_context.h" + +BEGIN_AS_NAMESPACE + +static asQWORD __attribute__((noinline)) CallX64(const asQWORD *args, const asQWORD *floatArgs, const int paramSize, asQWORD func) +{ + volatile asQWORD ret = 0; + + __asm__ __volatile__ ( + "# Move the parameters into registers before the rsp is modified\n" + "mov %1, %%r10\n" // r10 = args + "mov %2, %%r11\n" // r11 = floatArgs + "xor %%r12, %%r12\n" + "mov %3, %%r12d\n" + "mov %4, %%r14\n" // r14 = func + + "# Store the stack pointer in r15 since it is guaranteed not to change over a function call\n" + "mov %%rsp, %%r15\n" + + "# Allocate space on the stack for the arguments\n" + "# Make room for at least 4 arguments even if there are less. When\n" + "# the compiler does optimizations for speed it may use these for \n" + "# temporary storage.\n" + "mov %%r12, %%rdi\n" + "add $32,%%edi\n" + + "# Make sure the stack pointer is 16byte aligned so the\n" + "# whole program optimizations will work properly\n" + "# TODO: runtime optimize: Can this be optimized with fewer instructions?\n" + "mov %%rsp,%%rsi\n" + "sub %%rdi,%%rsi\n" + "and $0x8,%%rsi\n" + "add %%rsi,%%rdi\n" + "sub %%rdi,%%rsp\n" + + "# Jump straight to calling the function if no parameters\n" + "cmp $0,%%r12 # Compare paramSize with 0\n" + "je callfunc # Jump to call funtion if (paramSize == 0)\n" + + "# Copy arguments from script stack to application stack\n" + "# Order is (first to last):\n" + "# rcx, rdx, r8, r9 & everything else goes on stack\n" + "movq (%%r10),%%rcx\n" + "movq 8(%%r10),%%rdx\n" + "movq 16(%%r10),%%r8\n" + "movq 24(%%r10),%%r9\n" + + "# Negate the 4 params from the size to be copied\n" + "sub $32,%%r12d\n" + "js copyfloat # Jump if negative result\n" + "jz copyfloat # Jump if zero result\n" + + "# Now copy all remaining params onto stack allowing space for first four\n" + "# params to be flushed back to the stack if required by the callee.\n" + "add $32,%%r10 # Position input pointer 4 args ahead\n" + "mov %%rsp,%%r13 # Put the stack pointer into r13\n" + "add $32,%%r13 # Leave space for first 4 args on stack\n" + + "copyoverflow:\n" + "movq (%%r10),%%rdi # Read param from source stack into rdi\n" + "movq %%rdi,(%%r13) # Copy param to real stack\n" + "add $8,%%r13 # Move virtual stack pointer\n" + "add $8,%%r10 # Move source stack pointer\n" + "sub $8,%%r12d # Decrement remaining count\n" + "jnz copyoverflow # Continue if more params\n" + + "copyfloat:\n" + "# Any floating point params?\n" + "cmp $0,%%r11\n" + "je callfunc\n" + + "movlpd (%%r11),%%xmm0\n" + "movlpd 8(%%r11),%%xmm1\n" + "movlpd 16(%%r11),%%xmm2\n" + "movlpd 24(%%r11),%%xmm3\n" + + "callfunc:\n" + "call *%%r14\n" + + "# restore stack pointer\n" + "mov %%r15, %%rsp\n" + + "lea %0, %%rbx\n" // Load the address of the ret variable into rbx + "movq %%rax,(%%rbx)\n" // Copy the returned value into the ret variable + + : // no output + : "m" (ret), "r" (args), "r" (floatArgs), "r" (paramSize), "r" (func) + : "rdi", "rsi", "rsp", "rbx", "r10", "r11", "%r12", "r13", "r14", "r15" + ); + + return ret; +} + +static asDWORD GetReturnedFloat() +{ + volatile asDWORD ret = 0; + + __asm__ __volatile__ ( + "lea %0, %%rax\n" + "movss %%xmm0, (%%rax)" + : /* no output */ + : "m" (ret) + : "%rax" + ); + + return ret; +} + +static asQWORD GetReturnedDouble() +{ + volatile asQWORD ret = 0; + + __asm__ __volatile__ ( + "lea %0, %%rax\n" + "movlpd %%xmm0, (%%rax)" + : /* no optput */ + : "m" (ret) + : "%rax" + ); + + return ret; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + asUINT paramSize = 0; // QWords + void **vftable; + + asQWORD allArgBuffer[64]; + asQWORD floatArgBuffer[4]; + + int callConv = sysFunc->callConv; + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + + // Set the return pointer as the first argument + allArgBuffer[paramSize++] = (asQWORD)retPointer; + } + +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + if( callConv == ICC_THISCALL || + callConv == ICC_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) +#else + // Optimization to avoid check 12 values (all ICC_ that contains THISCALL) + if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) || + (callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) +#endif + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } + + if( callConv == ICC_CDECL_OBJFIRST || + callConv == ICC_CDECL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + else if( callConv == ICC_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } +#endif + +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + if( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) +#else + if( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) +#endif + { + // Get the true function pointer from the virtual function table + vftable = *(void***)obj; + func = vftable[asPWORD(func)>>3]; + } + + // Move the arguments to the buffer + asUINT dpos = paramSize; + asUINT spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { + if( descr->parameterTypes[n].GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE || + (descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK) ) + { + allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; + spos += AS_PTR_SIZE; + paramSize++; + } + else + { + // Copy the object's memory to the buffer + memcpy(&allArgBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos += AS_PTR_SIZE; + asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords(); + asUINT qwords = (dwords >> 1) + (dwords & 1); + dpos += qwords; + paramSize += qwords; + } + } + else if( descr->parameterTypes[n].GetTokenType() == ttQuestion ) + { + // Copy the reference and the type id + allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; + spos += 2; + allArgBuffer[dpos++] = args[spos++]; + paramSize += 2; + } + else + { + // Copy the value directly + asUINT dwords = descr->parameterTypes[n].GetSizeOnStackDWords(); + if( dwords > 1 ) + { + allArgBuffer[dpos] = *(asQWORD*)&args[spos]; + + // Double arguments are moved to a separate buffer in order to be placed in the XMM registers, + // though this is only done for first 4 arguments, the rest are placed on the stack + if( paramSize < 4 && descr->parameterTypes[n].IsDoubleType() ) + floatArgBuffer[dpos] = *(asQWORD*)&args[spos]; + + dpos++; + spos += 2; + } + else + { + allArgBuffer[dpos] = args[spos]; + + // Float arguments are moved to a separate buffer in order to be placed in the XMM registers, + // though this is only done for first 4 arguments, the rest are placed on the stack + if( paramSize < 4 && descr->parameterTypes[n].IsFloatType() ) + floatArgBuffer[dpos] = args[spos]; + + dpos++; + spos++; + } + + paramSize++; + } + } + + if( callConv == ICC_CDECL_OBJLAST || + callConv == ICC_CDECL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + else if( callConv == ICC_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } +#endif + + retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func); + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_X64_MSVC +#endif // AS_MAX_PORTABILITY + + diff --git a/3rdparty/angelscript/src/as_callfunc_x64_msvc.cpp b/3rdparty/angelscript/src/as_callfunc_x64_msvc.cpp new file mode 100644 index 0000000..8af5adc --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_x64_msvc.cpp @@ -0,0 +1,217 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// +// Added support for thiscall methods by Jordi Oliveras Rovira in April, 2014. +// + +#include + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X64_MSVC + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_context.h" + +BEGIN_AS_NAMESPACE + +// These functions are implemented in as_callfunc_x64_msvc.asm +extern "C" asQWORD CallX64(const asQWORD *args, const asQWORD *floatArgs, int paramSize, asQWORD func); +extern "C" asDWORD GetReturnedFloat(); +extern "C" asQWORD GetReturnedDouble(); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + asUINT paramSize = 0; // QWords + void **vftable; + + asQWORD allArgBuffer[64]; + asQWORD floatArgBuffer[4]; + + int callConv = sysFunc->callConv; + + // Optimization to avoid check 12 values (all ICC_ that contains THISCALL) + if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) || + (callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + + // Set the return pointer as the first argument + allArgBuffer[paramSize++] = (asQWORD)retPointer; + } + + if( callConv == ICC_CDECL_OBJFIRST || + callConv == ICC_CDECL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } + else if( callConv == ICC_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } + + if( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Get the true function pointer from the virtual function table + vftable = *(void***)obj; + func = vftable[asPWORD(func)>>2]; + } + + // Move the arguments to the buffer + asUINT dpos = paramSize; + asUINT spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + asCDataType &dt = descr->parameterTypes[n]; + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) + { + if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE || + (dt.GetTypeInfo()->flags & COMPLEX_MASK) ) + { + allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; + spos += AS_PTR_SIZE; + paramSize++; + } + else + { + // Copy the object's memory to the buffer + memcpy(&allArgBuffer[dpos], *(void**)(args+spos), dt.GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos += AS_PTR_SIZE; + asUINT dwords = dt.GetSizeInMemoryDWords(); + asUINT qwords = (dwords >> 1) + (dwords & 1); + dpos += qwords; + paramSize += qwords; + } + } + else if( dt.GetTokenType() == ttQuestion ) + { + // Copy the reference and the type id + allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; + spos += 2; + allArgBuffer[dpos++] = args[spos++]; + paramSize += 2; + } + else + { + // Copy the value directly + asUINT dwords = dt.GetSizeOnStackDWords(); + if( dwords > 1 ) + { + allArgBuffer[dpos] = *(asQWORD*)&args[spos]; + + // Double arguments are moved to a separate buffer in order to be placed in the XMM registers, + // though this is only done for first 4 arguments, the rest are placed on the stack + if( paramSize < 4 && dt.IsDoubleType() ) + floatArgBuffer[dpos] = *(asQWORD*)&args[spos]; + + dpos++; + spos += 2; + } + else + { + allArgBuffer[dpos] = args[spos]; + + // Float arguments are moved to a separate buffer in order to be placed in the XMM registers, + // though this is only done for first 4 arguments, the rest are placed on the stack + if( paramSize < 4 && dt.IsFloatType() ) + floatArgBuffer[dpos] = args[spos]; + + dpos++; + spos++; + } + + paramSize++; + } + } + + if( callConv == ICC_CDECL_OBJLAST || + callConv == ICC_CDECL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)obj; + } + else if( callConv == ICC_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } + + retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func); + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_X64_MSVC +#endif // AS_MAX_PORTABILITY + + diff --git a/3rdparty/angelscript/src/as_callfunc_x64_msvc_asm.asm b/3rdparty/angelscript/src/as_callfunc_x64_msvc_asm.asm new file mode 100644 index 0000000..f4cd1ac --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_x64_msvc_asm.asm @@ -0,0 +1,208 @@ +; +; AngelCode Scripting Library +; Copyright (c) 2003-2011 Andreas Jonsson +; +; 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. +; +; The original version of this library can be located at: +; http://www.angelcode.com/angelscript/ +; +; Andreas Jonsson +; andreas@angelcode.com +; + +.code +PUBLIC CallX64 + +; asQWORD CallX64(const asQWORD *args, const asQWORD *floatArgs, int paramSize, asQWORD func) + +CallX64 PROC FRAME + + ; PROLOG + + ; We must save preserved registers that are used + ; TODO: No need to save unused registers + + push rbp +.pushreg rbp + push rsi +.pushreg rsi + push r11 +.pushreg r11 + push rdi +.pushreg rdi + push r12 +.pushreg r12 + push r13 +.pushreg r13 + push r14 +.pushreg r14 + push r15 +.pushreg r15 + push rbx +.pushreg rbx + sub rsp, 050h +.allocstack 050h + mov rbp, rsp +.setframe rbp, 0 +.endprolog + + ; Move function param to non-scratch register + mov r14, r9 ; r14 = function + + ; Allocate space on the stack for the arguments + ; Make room for at least 4 arguments even if there are less. When + ; the compiler does optimizations for speed it may use these for + ; temporary storage. + mov rdi, r8 + add rdi, 32 + + ; Make sure the stack pointer is 16byte aligned so the + ; whole program optimizations will work properly + ; TODO: optimize: Can this be optimized with fewer instructions? + mov rsi, rsp + sub rsi, rdi + and rsi, 8h + add rdi, rsi + sub rsp, rdi + + ; Jump straight to calling the function if no parameters + cmp r8d, 0 ; Compare paramSize with 0 + je callfunc ; Jump to call funtion if (paramSize == 0) + + ; Move params to non-scratch registers + mov rsi, rcx ; rsi = pArgs + mov r11, rdx ; r11 = pFloatArgs (can be NULL) + mov r12d, r8d ; r12 = paramSize + + ; Copy arguments from script stack to application stack + ; Order is (first to last): + ; rcx, rdx, r8, r9 & everything else goes on stack + mov rcx, qword ptr [rsi] + mov rdx, qword ptr [rsi + 8] + mov r8, qword ptr [rsi + 16] + mov r9, qword ptr [rsi + 24] + + ; Negate the 4 params from the size to be copied + sub r12d, 32 + js copyfloat ; Jump if negative result + jz copyfloat ; Jump if zero result + + ; Now copy all remaining params onto stack allowing space for first four + ; params to be flushed back to the stack if required by the callee. + + add rsi, 32 ; Position input pointer 4 args ahead + mov r13, rsp ; Put the stack pointer into r13 + add r13, 32 ; Leave space for first 4 args on stack + +copyoverflow: + mov r15, qword ptr [rsi] ; Read param from source stack into r15 + mov qword ptr [r13], r15 ; Copy param to real stack + add r13, 8 ; Move virtual stack pointer + add rsi, 8 ; Move source stack pointer + sub r12d, 8 ; Decrement remaining count + jnz copyoverflow ; Continue if more params + +copyfloat: + ; Any floating point params? + cmp r11, 0 + je callfunc + + movlpd xmm0, qword ptr [r11] + movlpd xmm1, qword ptr [r11 + 8] + movlpd xmm2, qword ptr [r11 + 16] + movlpd xmm3, qword ptr [r11 + 24] + +callfunc: + + ; Call function + call r14 + + ; Restore the stack + mov rsp, rbp + + ; EPILOG: Restore stack & preserved registers + add rsp, 050h + pop rbx + pop r15 + pop r14 + pop r13 + pop r12 + pop rdi + pop r11 + pop rsi + pop rbp + + ; return value in RAX + ret + +CallX64 ENDP + + +PUBLIC GetReturnedFloat + +; asDWORD GetReturnedFloat() + +GetReturnedFloat PROC FRAME + + ; PROLOG: Store registers and allocate stack space + + sub rsp, 8 ; We'll need 4 bytes for temporary storage (8 bytes with alignment) +.allocstack 8 +.endprolog + + ; Move the float value from the XMM0 register to RAX register + movss dword ptr [rsp], xmm0 + mov eax, dword ptr [rsp] + + ; EPILOG: Clean up + + add rsp, 8 + + ret + +GetReturnedFloat ENDP + + +PUBLIC GetReturnedDouble + +; asDWORD GetReturnedDouble() + +GetReturnedDouble PROC FRAME + + ; PROLOG: Store registers and allocate stack space + + sub rsp, 8 ; We'll need 8 bytes for temporary storage +.allocstack 8 +.endprolog + + ; Move the double value from the XMM0 register to the RAX register + movlpd qword ptr [rsp], xmm0 + mov rax, qword ptr [rsp] + + ; EPILOG: Clean up + + add rsp, 8 + + ret + +GetReturnedDouble ENDP + +END \ No newline at end of file diff --git a/3rdparty/angelscript/src/as_callfunc_x86.cpp b/3rdparty/angelscript/src/as_callfunc_x86.cpp new file mode 100644 index 0000000..0ac690e --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_x86.cpp @@ -0,0 +1,1514 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_x86.cpp +// +// These functions handle the actual calling of system functions +// +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. +// + + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X86 + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +BEGIN_AS_NAMESPACE + +// +// With some compile level optimizations the functions don't clear the FPU +// stack themselves. So we have to do it as part of calling the native functions, +// as the compiler will not be able to predict when it is supposed to do it by +// itself due to the dynamic nature of scripts +// +// - fninit clears the FPU stack and the FPU control word +// - emms only clears the FPU stack, while preserving the FPU control word +// +// By default I use fninit as it seems to be what works for most people, +// but some may find it necessary to define this as emms instead. +// +// TODO: Figure out when one or the other must be used, and a way to +// configure this automatically in as_config.h +// +#ifndef CLEAR_FPU_STACK +#define CLEAR_FPU_STACK fninit +#endif + +// These macros are just to allow me to use the above macro in the GNUC style inline assembly +#define _S(x) _TOSTRING(x) +#define _TOSTRING(x) #x + +// Prototypes +asQWORD CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func); +asQWORD CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func); +asQWORD CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func); +asQWORD CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr); +asQWORD CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr); +asQWORD CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr); +asQWORD CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func); +asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func); +asQWORD CallThisCallFunctionRetByRef(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr); + +asDWORD GetReturnedFloat(); +asQWORD GetReturnedDouble(); + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) +{ + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + asQWORD retQW = 0; + + // Prepare the parameters + asDWORD paramBuffer[64]; + int callConv = sysFunc->callConv; + + // Changed because need check for ICC_THISCALL_OBJFIRST or + // ICC_THISCALL_OBJLAST if sysFunc->takesObjByVal (avoid copy code) + // Check if is THISCALL_OBJ* calling convention (in this case needs to add secondObject pointer into stack). + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + int paramSize = isThisCallMethod || sysFunc->takesObjByVal ? 0 : sysFunc->paramSize; + + int dpos = 1; + + if( isThisCallMethod && + (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + if( sysFunc->takesObjByVal || isThisCallMethod ) + { + int spos = 0; + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + // TODO: bug: Must call the object's copy constructor instead of doing a memcpy, + // as the object may hold a pointer to itself. It's not enough to + // change only this memcpy as the assembler routine also makes a copy + // of paramBuffer to the final stack location. To avoid the second + // copy the C++ routine should point paramBuffer to the final stack + // position and copy the values directly to that location. The assembler + // routines then don't need to copy anything, and will just be + // responsible for setting up the registers and the stack frame appropriately. + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + if( isThisCallMethod && + (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + // Add the object pointer as the last parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + // Make the actual call + asFUNCTION_t func = sysFunc->func; + if( sysFunc->hostReturnInMemory ) + callConv++; + + switch( callConv ) + { + case ICC_CDECL: + retQW = CallCDeclFunction(args, paramSize<<2, func); + break; + + case ICC_CDECL_RETURNINMEM: + retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, func, retPointer); + break; + + case ICC_STDCALL: + retQW = CallSTDCallFunction(args, paramSize<<2, func); + break; + + case ICC_STDCALL_RETURNINMEM: + // Push the return pointer on the stack + paramSize++; + args--; + *(asPWORD*)args = (size_t)retPointer; + + retQW = CallSTDCallFunction(args, paramSize<<2, func); + break; + + case ICC_THISCALL: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: + retQW = CallThisCallFunction(obj, args, paramSize<<2, func); + break; + + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer); + break; + + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: + { + // Get virtual function table from the object pointer + asFUNCTION_t *vftable = *(asFUNCTION_t**)obj; + retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]); + } + break; + + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + { + // Get virtual function table from the object pointer + asFUNCTION_t *vftable = *(asFUNCTION_t**)obj; + retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], retPointer); + } + break; + + case ICC_CDECL_OBJLAST: + retQW = CallCDeclFunctionObjLast(obj, args, paramSize<<2, func); + break; + + case ICC_CDECL_OBJLAST_RETURNINMEM: + // Call the system object method as a cdecl with the obj ref as the last parameter + retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, func, retPointer); + break; + + case ICC_CDECL_OBJFIRST: + // Call the system object method as a cdecl with the obj ref as the first parameter + retQW = CallCDeclFunctionObjFirst(obj, args, paramSize<<2, func); + break; + + case ICC_CDECL_OBJFIRST_RETURNINMEM: + // Call the system object method as a cdecl with the obj ref as the first parameter + retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, func, retPointer); + break; + + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +// On GCC we need to prevent the compiler from inlining these assembler routines when +// optimizing for speed (-O3), as the loop labels get duplicated which cause compile errors. + +#ifdef __GNUC__ + #define NOINLINE __attribute ((__noinline__)) +#else + #define NOINLINE +#endif + + +asQWORD NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + // It is not possible to rely on ESP or BSP to refer to variables or arguments on the stack + // depending on compiler settings BSP may not even be used, and the ESP is not always on the + // same offset from the local variables. Because the code adjusts the ESP register it is not + // possible to inform the arguments through symbolic names below. + + // It's not also not possible to rely on the memory layout of the function arguments, because + // on some compiler versions and settings the arguments may be copied to local variables with a + // different ordering before they are accessed by the rest of the code. + + // I'm copying the arguments into this array where I know the exact memory layout. The address + // of this array will then be passed to the inline asm in the EDX register. + volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)}; + + asm __volatile__( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 4(%%ebx), %%eax \n" // paramSize + "addl $4, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + // Copy all arguments to the stack and call the function + "movl 4(%%ebx), %%ecx \n" // paramSize + "movl 0(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy \n" + "copyloop: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop \n" + "endcopy: \n" + "call *8(%%ebx) \n" + "addl 4(%%ebx), %%esp \n" // pop arguments + + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Push the object pointer as the last argument to the function + push obj + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $8, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "pushl 0(%%ebx) \n" // obj + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy8 \n" + "copyloop8: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop8 \n" + "endcopy8: \n" + "call *12(%%ebx) \n" + "addl 8(%%ebx), %%esp \n" // pop arguments + "addl $4, %%esp \n" // pop obj + + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // push object as first parameter + push obj + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $8, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy6 \n" + "copyloop6: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop6 \n" + "endcopy6: \n" + "pushl 0(%%ebx) \n" // push obj + "call *12(%%ebx) \n" + "addl 8(%%ebx), %%esp \n" // pop arguments + "addl $4, %%esp \n" // pop obj + + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the object pointer + push obj + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 8 +#else + add esp, 4 +#endif + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $12, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy5 \n" + "copyloop5: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop5 \n" + "endcopy5: \n" + "pushl 0(%%ebx) \n" // push object first + "pushl 16(%%ebx) \n" // retPtr + "call *12(%%ebx) \n" // func + "addl 8(%%ebx), %%esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $8, %%esp \n" // Pop the return pointer and object pointer +#else + "addl $4, %%esp \n" // Pop the object pointer +#endif + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); +#endif + + return retQW; +} + +asQWORD NOINLINE CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 4 +#endif + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 4(%%ebx), %%eax \n" // paramSize + "addl $8, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 4(%%ebx), %%ecx \n" // paramSize + "movl 0(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy7 \n" + "copyloop7: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop7 \n" + "endcopy7: \n" + "pushl 12(%%ebx) \n" // retPtr + "call *8(%%ebx) \n" // func + "addl 4(%%ebx), %%esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $4, %%esp \n" // Pop the return pointer +#endif + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + push obj + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 4 +#endif + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $12, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "pushl 0(%%ebx) \n" // obj + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy4 \n" + "copyloop4: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop4 \n" + "endcopy4: \n" + "pushl 16(%%ebx) \n" // retPtr + "call *12(%%ebx) \n" // func + "addl 8(%%ebx), %%esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $8, %%esp \n" // Pop the return pointer and object pointer +#else + "addl $4, %%esp \n" // Pop the object pointer +#endif + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // The callee already removed parameters from the stack + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 4(%%ebx), %%eax \n" // paramSize + "addl $4, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 4(%%ebx), %%ecx \n" // paramSize + "movl 0(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy2 \n" + "copyloop2: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop2 \n" + "endcopy2: \n" + "call *8(%%ebx) \n" // callee pops the arguments + + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + + +asQWORD NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Push the object pointer on the stack + push obj +#else + // Move object pointer to ECX + mov ecx, obj +#endif + + // Call function + call [func] + +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + // Pop arguments + add esp, paramSize +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Pop object pointer + add esp, 4 +#endif +#endif + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $8, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push all arguments on the stack + "cmp $0, %%ecx \n" + "je endcopy1 \n" + "copyloop1: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop1 \n" + "endcopy1: \n" + "movl 0(%%ebx), %%ecx \n" // move obj into ECX +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + "pushl %%ecx \n" // push obj on the stack +#endif + "call *12(%%ebx) \n" +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + "addl 8(%%ebx), %%esp \n" // pop arguments +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + "addl $4, %%esp \n" // pop obj +#endif +#endif + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asQWORD NOINLINE CallThisCallFunctionRetByRef(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr) +{ + volatile asQWORD retQW = 0; + +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + CLEAR_FPU_STACK + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Push the object pointer on the stack + push obj +#else + // Move object pointer to ECX + mov ecx, obj +#endif + + // Push the return pointer + push retPtr + + // Call function + call [func] + +#ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 4 +#endif + +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + // Pop arguments + add esp, paramSize +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Pop object pointer + add esp, 4 +#endif +#endif + + // Copy return value from EAX:EDX + lea ecx, retQW + mov [ecx], eax + mov 4[ecx], edx + + // Restore registers + pop ecx + } + +#elif defined ASM_AT_N_T + + volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)}; + + asm __volatile__ ( +#ifdef __OPTIMIZE__ + // When compiled with optimizations the stack unwind doesn't work properly, + // causing exceptions to crash the application. By adding this prologue + // and the epilogue below, the stack unwind works as it should. + // TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below + "pushl %%ebp \n" + ".cfi_adjust_cfa_offset 4 \n" + ".cfi_rel_offset ebp, 0 \n" + "movl %%esp, %%ebp \n" + ".cfi_def_cfa_register ebp \n" +#endif + _S(CLEAR_FPU_STACK) "\n" + "pushl %%ebx \n" + "movl %%edx, %%ebx \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 8(%%ebx), %%eax \n" // paramSize + "addl $12, %%eax \n" // counting esp that we will push on the stack + "movl %%esp, %%ecx \n" + "subl %%eax, %%ecx \n" + "andl $15, %%ecx \n" + "movl %%esp, %%eax \n" + "subl %%ecx, %%esp \n" + "pushl %%eax \n" // Store the original stack pointer + + "movl 8(%%ebx), %%ecx \n" // paramSize + "movl 4(%%ebx), %%eax \n" // args + "addl %%ecx, %%eax \n" // push all arguments to the stack + "cmp $0, %%ecx \n" + "je endcopy3 \n" + "copyloop3: \n" + "subl $4, %%eax \n" + "pushl (%%eax) \n" + "subl $4, %%ecx \n" + "jne copyloop3 \n" + "endcopy3: \n" +#ifdef AS_MINGW47 + // MinGW made some strange choices with 4.7 and the thiscall calling convention, + // returning an object in memory is completely different from when not returning + // in memory + "pushl 0(%%ebx) \n" // push obj on the stack + "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX + "call *12(%%ebx) \n" // call the function +#else + "movl 0(%%ebx), %%ecx \n" // move obj into ECX +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + "pushl %%ecx \n" // push obj on the stack +#endif + "pushl 16(%%ebx) \n" // push retPtr on the stack + "call *12(%%ebx) \n" +#ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $4, %%esp \n" // pop return pointer +#endif +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + "addl 8(%%ebx), %%esp \n" // pop arguments +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + "addl $4, %%esp \n" // pop the object pointer +#endif +#endif +#endif // AS_MINGW47 + // Pop the alignment bytes + "popl %%esp \n" + "popl %%ebx \n" +#ifdef __OPTIMIZE__ + // Epilogue + "movl %%ebp, %%esp \n" + ".cfi_def_cfa_register esp \n" + "popl %%ebp \n" + ".cfi_adjust_cfa_offset -4 \n" + ".cfi_restore ebp \n" +#endif + // Copy EAX:EDX to retQW. As the stack pointer has been + // restored it is now safe to access the local variable + "leal %1, %%ecx \n" + "movl %%eax, 0(%%ecx) \n" + "movl %%edx, 4(%%ecx) \n" + : // output + : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument + : "%eax", "%ecx" // clobber + ); + +#endif + + return retQW; +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + +#if defined ASM_INTEL + + // Get the float value from ST0 + __asm fstp dword ptr [f] + +#elif defined ASM_AT_N_T + + asm("fstps %0 \n" : "=m" (f)); + +#endif + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d; + +#if defined ASM_INTEL + + // Get the double value from ST0 + __asm fstp qword ptr [d] + +#elif defined ASM_AT_N_T + + asm("fstpl %0 \n" : "=m" (d)); + +#endif + + return d; +} + +END_AS_NAMESPACE + +#endif // AS_X86 +#endif // AS_MAX_PORTABILITY + + + + diff --git a/3rdparty/angelscript/src/as_callfunc_xenon.cpp b/3rdparty/angelscript/src/as_callfunc_xenon.cpp new file mode 100644 index 0000000..c52055e --- /dev/null +++ b/3rdparty/angelscript/src/as_callfunc_xenon.cpp @@ -0,0 +1,737 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_xenon.cpp +// +// These functions handle the actual calling of system functions +// +// This version is Xenon specific +// Modified from as_callfunc_ppc.cpp by Laszlo Perneky February 2007 +// +// Modified by Cyril Tissier March 2010: +// various fixes in 'float' args passing / function return +// properly handling 'double' type +// various fixes in asm ppcFunc +// fix for variable arguments +// +// Modified by Anthony Clark May 2015 +// Fixed the issue where int64 and uint64 could not be passed nativly +// few minor fixes within asm ppcFunc to handle int64 and uint64 + + +// XBox 360 calling convention +// =========================== +// I've yet to find an official document with the ABI for XBox 360, +// but I'll describe what I've gathered from the code and tests +// performed by the AngelScript community. +// +// Arguments are passed in the following registers: +// r3 - r10 : integer/pointer arguments (each register is 64bit) +// fr1 - fr13 : float/double arguments (each register is 64bit) +// +// Arguments that don't fit in the registers will be pushed on the stack. +// +// When a float or double is passed as argument, its value will be placed +// in the next available float register, but it will also reserve general +// purpose register. +// +// Example: void foo(float a, int b). a will be passed in fr1 and b in r4. +// +// For each argument passed to a function an 8byte slot is reserved on the +// stack, so that the function can offload the value there if needed. The +// first slot is at r1+20, the next at r1+28, etc. +// +// If the function is a class method, the this pointer is passed as hidden +// first argument. If the function returns an object in memory, the address +// for that memory is passed as hidden first argument. +// +// Return value are placed in the following registers: +// r3 : integer/pointer values +// fr1 : float/double values +// +// Rules for registers +// r1 : stack pointer +// r14-r31 : nonvolatile, i.e. their values must be preserved +// fr14-fr31 : nonvolatile, i.e. their values must be preserved +// r0, r2, r13 : dedicated. I'm not sure what it means, but it is probably best not to use them +// +// The stack pointer must always be aligned at 8 bytes. +// +// References: +// https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF77852569970071B0D6/$file/eabi_app.pdf +// +// TODO: The code doesn't handle objects passed by value (unless they are max 4 bytes in size) + + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#if defined(AS_XENON) + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" +#include "as_context.h" + +#include +#include +#include + +BEGIN_AS_NAMESPACE + +#define AS_PPC_MAX_ARGS 32 +#define AS_PPC_THISCALL_REG 1 +#define AS_PPC_RETURNINMEM_REG 1 +#define AS_PPC_ENDOFARGS 1 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register type to load, or zero if end of arguments +enum argTypes +{ + ppcENDARG = 0, + ppcINTARG = 1, + ppcFLOATARG = 2, + ppcDOUBLEARG = 3 +}; + +// Loads all data into the correct places and calls the function. +// pArgs is the array of the argument values +// pArgTypes is an array containing a byte indicating the type (enum argTypes) for each argument. +// dwFunc is the address of the function that will be called +asQWORD __declspec( naked ) ppcFunc(const asQWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes) +{ + __asm + { +_ppcFunc: + // Prologue + // Read and stack the link register (return address) + mflr r12 + stw r12,-8(r1) + + // Backup all non-volatile registers we use in this function + std r31,-10h(r1) // stack pointer for pushing arguments + std r27,-18h(r1) // dwFunc + std r26,-20h(r1) // pArgs + std r25,-28h(r1) // pArgTypes + std r24,-30h(r1) // current arg type + std r23,-38h(r1) // counter for used GPRs + std r22,-40h(r1) // counter for used float registers + + // Setup the stack frame to make room for the backup of registers + // and the arguments that will be passed to the application function. + // 512 bytes is enough for about 50 arguments plus backup of 8 + // TODO: Should perhaps make this dynamic based on number of arguments + stwu r1,-200h(r1) + +////////////////////////////////////////////////////////////////////////// +// Initialize local variables +////////////////////////////////////////////////////////////////////////// + + // r31 is our pointer into the stack where the arguments will be place + // The MSVC optimizer seems to rely on nobody copying the r1 register directly + // so we can't just do a simple 'addi r31, r1, 14h' as the optimizer may + // end up moving this instruction to before the update of r1 above. + // Instead we'll read the previous stack pointer from the stack, and then + // subtract to get the correct offset. + lwz r31, 0(r1) + subi r31, r31, 1ECh // prev r1 - 512 + 20 = curr r1 + 20 + + mr r26, r3 // pArgs + mr r27, r4 // dwFunc + mr r25, r5 // pArgTypes + + // Counting of used/assigned GPR's + sub r23, r23, r23 + // Counting of used/assigned Float Registers + sub r22, r22, r22 + + // Begin loading and stacking registers + subi r25, r25, 1 + +////////////////////////////////////////////////////////////////////////// +// Fetch the next argument +////////////////////////////////////////////////////////////////////////// +ppcNextArg: + // Increment rArgTypePtr + addi r25, r25, 1 + // Get data type + lbz r24, 0(r25) + + // r24 holds the data type + cmplwi cr6, r24, 0 + beq cr6, ppcArgsEnd + cmplwi cr6, r24, 1 + beq cr6, ppcArgIsInteger + cmplwi cr6, r24, 2 + beq cr6, ppcArgIsFloat + cmplwi cr6, r24, 3 + beq cr6, ppcArgIsDouble + +////////////////////////////////////////////////////////////////////////// +// Load and stack integer arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsInteger: + // Get the arg from the stack + ld r12, 0(r26) + + // r23 holds the integer arg count so far + cmplwi cr6, r23, 0 + beq cr6, ppcLoadIntReg0 + cmplwi cr6, r23, 1 + beq cr6, ppcLoadIntReg1 + cmplwi cr6, r23, 2 + beq cr6, ppcLoadIntReg2 + cmplwi cr6, r23, 3 + beq cr6, ppcLoadIntReg3 + cmplwi cr6, r23, 4 + beq cr6, ppcLoadIntReg4 + cmplwi cr6, r23, 5 + beq cr6, ppcLoadIntReg5 + cmplwi cr6, r23, 6 + beq cr6, ppcLoadIntReg6 + cmplwi cr6, r23, 7 + beq cr6, ppcLoadIntReg7 + + // no more than 8 parameters + b ppcLoadIntRegUpd + + ppcLoadIntReg0: + mr r3, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg1: + mr r4, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg2: + mr r5, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg3: + mr r6, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg4: + mr r7, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg5: + mr r8, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg6: + mr r9, r12 + b ppcLoadIntRegUpd + ppcLoadIntReg7: + mr r10, r12 + b ppcLoadIntRegUpd + + ppcLoadIntRegUpd: + std r12, 0(r31) // push on the stack + addi r31, r31, 8 // inc stack by 1 reg + + addi r23, r23, 1 // Increment used int register count + addi r26, r26, 8 // Increment pArgs + b ppcNextArg // Call next arg + +////////////////////////////////////////////////////////////////////////// +// Load and stack float arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsFloat: + // Get the arg from the stack + lfs fr0, 0(r26) + + // r22 holds the float arg count so far + cmplwi cr6, r22, 0 + beq cr6, ppcLoadFloatReg0 + cmplwi cr6, r22, 1 + beq cr6, ppcLoadFloatReg1 + cmplwi cr6, r22, 2 + beq cr6, ppcLoadFloatReg2 + cmplwi cr6, r22, 3 + beq cr6, ppcLoadFloatReg3 + cmplwi cr6, r22, 4 + beq cr6, ppcLoadFloatReg4 + cmplwi cr6, r22, 5 + beq cr6, ppcLoadFloatReg5 + cmplwi cr6, r22, 6 + beq cr6, ppcLoadFloatReg6 + cmplwi cr6, r22, 7 + beq cr6, ppcLoadFloatReg7 + cmplwi cr6, r22, 8 + beq cr6, ppcLoadFloatReg8 + cmplwi cr6, r22, 9 + beq cr6, ppcLoadFloatReg9 + cmplwi cr6, r22, 10 + beq cr6, ppcLoadFloatReg10 + cmplwi cr6, r22, 11 + beq cr6, ppcLoadFloatReg11 + cmplwi cr6, r22, 12 + beq cr6, ppcLoadFloatReg12 + + // no more than 12 parameters + b ppcLoadFloatRegUpd + + ppcLoadFloatReg0: + fmr fr1, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg1: + fmr fr2, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg2: + fmr fr3, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg3: + fmr fr4, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg4: + fmr fr5, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg5: + fmr fr6, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg6: + fmr fr7, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg7: + fmr fr8, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg8: + fmr fr9, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg9: + fmr fr10, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg10: + fmr fr11, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg11: + fmr fr12, fr0 + b ppcLoadFloatRegUpd + ppcLoadFloatReg12: + fmr fr13, fr0 + b ppcLoadFloatRegUpd + + ppcLoadFloatRegUpd: + stfs fr0, 0(r31) // push on the stack + addi r31, r31, 8 // inc stack by 1 reg + + addi r22, r22, 1 // Increment used float register count + addi r23, r23, 1 // Increment used int register count - a float reg eats up a GPR + addi r26, r26, 4 // Increment pArgs + b ppcNextArg // Call next arg + +////////////////////////////////////////////////////////////////////////// +// Load and stack double float arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsDouble: + // Get the arg from the stack + lfd fr0, 0(r26) + + // r22 holds the float arg count so far + cmplwi cr6, r22, 0 + beq cr6, ppcLoadDoubleReg0 + cmplwi cr6, r22, 1 + beq cr6, ppcLoadDoubleReg1 + cmplwi cr6, r22, 2 + beq cr6, ppcLoadDoubleReg2 + cmplwi cr6, r22, 3 + beq cr6, ppcLoadDoubleReg3 + cmplwi cr6, r22, 4 + beq cr6, ppcLoadDoubleReg4 + cmplwi cr6, r22, 5 + beq cr6, ppcLoadDoubleReg5 + cmplwi cr6, r22, 6 + beq cr6, ppcLoadDoubleReg6 + cmplwi cr6, r22, 7 + beq cr6, ppcLoadDoubleReg7 + cmplwi cr6, r22, 8 + beq cr6, ppcLoadDoubleReg8 + cmplwi cr6, r22, 9 + beq cr6, ppcLoadDoubleReg9 + cmplwi cr6, r22, 10 + beq cr6, ppcLoadDoubleReg10 + cmplwi cr6, r22, 11 + beq cr6, ppcLoadDoubleReg11 + cmplwi cr6, r22, 12 + beq cr6, ppcLoadDoubleReg12 + + // no more than 12 parameters + b ppcLoadDoubleRegUpd + + ppcLoadDoubleReg0: + fmr fr1, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg1: + fmr fr2, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg2: + fmr fr3, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg3: + fmr fr4, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg4: + fmr fr5, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg5: + fmr fr6, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg6: + fmr fr7, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg7: + fmr fr8, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg8: + fmr fr9, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg9: + fmr fr10, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg10: + fmr fr11, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg11: + fmr fr12, fr0 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg12: + fmr fr13, fr0 + b ppcLoadDoubleRegUpd + + ppcLoadDoubleRegUpd: + stfd fr0, 0(r31) // push on the stack + addi r31, r31, 8 // inc stack by 1 reg + + addi r22, r22, 1 // Increment used float register count + addi r23, r23, 1 // Increment used int register count + addi r26, r26, 8 // Increment pArgs + b ppcNextArg + +////////////////////////////////////////////////////////////////////////// +// Finished +////////////////////////////////////////////////////////////////////////// +ppcArgsEnd: + // Call the function + mtctr r27 + bctrl + + // Epilogue + // Restore callers stack + addi r1, r1, 200h + + // restore all registers we used in this fct + ld r22,-40h(r1) + ld r23,-38h(r1) + ld r24,-30h(r1) + ld r25,-28h(r1) + ld r26,-20h(r1) + ld r27,-18h(r1) + ld r31,-10h(r1) + + // Fetch return link to caller + lwz r12,-8(r1) + mtlr r12 + blr + } +} + +asDWORD GetReturnedFloat() +{ + // This variable must be declared volatile so that the + // compiler optimizations do not remove its initialization + // with the fr1 register due to believing the fr1 register + // isn't initialized. + volatile asDWORD f; + + __asm + { + stfs fr1, f + } + + return f; +} + +asQWORD GetReturnedDouble() +{ + // This variable must be declared volatile so that the + // compiler optimizations do not remove its initialization + // with the fr1 register due to believing the fr1 register + // isn't initialized. + volatile asQWORD f; + + __asm + { + stfd fr1, f + } + + return f; +} + +// returns true if the given parameter is a 'variable argument' +inline bool IsVariableArgument( asCDataType type ) +{ + return (type.GetTokenType() == ttQuestion) ? true : false; +} + +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) +{ + // TODO: Xenon does not yet support THISCALL_OBJFIRST/LAST + + asCScriptEngine *engine = context->m_engine; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + asDWORD *vftable; + + // Pack the arguments into an array that ppcFunc() can use to load each CPU register properly + asBYTE ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS]; + asQWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; + int argsCnt = 0; + + // If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3) + if( sysFunc->hostReturnInMemory ) + { + ppcArgs[argsCnt] = (asDWORD)retPointer; + ppcArgsType[argsCnt] = ppcINTARG; + argsCnt++; + } + + // If we have an object and it's not objectlast, then we put it as the first arg + if ( obj && + callConv != ICC_CDECL_OBJLAST && + callConv != ICC_CDECL_OBJLAST_RETURNINMEM ) + { + ppcArgs[argsCnt] = (asDWORD)obj; + ppcArgsType[argsCnt] = ppcINTARG; + argsCnt++; + } + + // If the function takes any objects by value, they must be copied + // to the stack, shifting the other arguments as necessary. paramBuffer + // will then replace the args pointer that was received from the VM. + // TODO: Is this really how XBox 360 passes objects by value? + asDWORD paramBuffer[AS_PPC_MAX_ARGS]; + if( sysFunc->takesObjByVal ) + { + int paramSize = 0; + int spos = 0; + int dpos = 1; + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + // Parameter object by value + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsObjectHandle() && + !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[dpos], *(void**)(args + spos), descr->parameterTypes[n].GetSizeInMemoryBytes() ); + + // Delete the original memory + engine->CallFree(*(char**)(args + spos)); + + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + // If this was a variable argument parameter, then account for the implicit typeId + if( IsVariableArgument( descr->parameterTypes[n] ) ) + { + // the TypeId is just a DWORD + paramBuffer[dpos++] = args[spos++]; + ++paramSize; + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[1]; + + asASSERT( paramSize <= AS_PPC_MAX_ARGS ); + } + + + const asUINT paramCount = (asUINT)descr->parameterTypes.GetLength(); + + asBYTE * pCurArgType = (asBYTE*)&ppcArgsType[argsCnt]; + asBYTE * pCurFixedArgValue = (asBYTE*)&ppcArgs[argsCnt]; + asBYTE * pCurStackArgValue = (asBYTE*)args; + + for( asUINT n = 0; n < paramCount; n++ ) + { + argsCnt++; + + if (descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference()) + { + *pCurArgType++ = ppcFLOATARG; + + *((float*) pCurFixedArgValue) = *((float*) pCurStackArgValue); + + pCurFixedArgValue += 4; + pCurStackArgValue += 4; + } + else if (descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference()) + { + *pCurArgType++ = ppcDOUBLEARG; + + *((double*) pCurFixedArgValue) = *((double*) pCurStackArgValue); + + pCurFixedArgValue += 8; + pCurStackArgValue += 8; + } + else + { + // TODO: The code also ignore the fact that large objects + // passed by value has been copied to the stack + // in the above loop. + + *pCurArgType++ = ppcINTARG; + + *((asQWORD*) pCurFixedArgValue) = *((asUINT*) pCurStackArgValue); + + if( !descr->parameterTypes[n].IsReference() ) + { + // If the arg is not 4 bytes which we coppied, lets do it again the right way + asUINT numBytes = descr->parameterTypes[n].GetSizeInMemoryBytes(); + if( numBytes == 1 ) + { + *((asQWORD*) pCurFixedArgValue) = *((asBYTE*) pCurStackArgValue); + } + else if( numBytes == 2 ) + { + *((asQWORD*) pCurFixedArgValue) = *((asWORD*) pCurStackArgValue); + } + else if( numBytes == 8 ) + { + *((asQWORD*) pCurFixedArgValue) = *((asQWORD*) pCurStackArgValue); + pCurStackArgValue += 4; // Increase our cur stack arg value by 4 bytes to = 8 total later + } + } + + pCurFixedArgValue += 8; + pCurStackArgValue += 4; + + // if it is a variable argument, account for the typeId + // implicitly add another parameter (AFTER the parameter above) for the typeId + if( IsVariableArgument(descr->parameterTypes[n]) ) + { + argsCnt++; + + *pCurArgType++ = ppcINTARG; + + *((int*) pCurFixedArgValue) = *((int*) pCurStackArgValue); + + pCurFixedArgValue += 4; + pCurStackArgValue += 4; + } + } + } + + // Add the arg list end indicator + ppcArgsType[argsCnt] = ppcENDARG; + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + { + retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType ); + break; + } + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + { + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = ppcFunc( ppcArgs, vftable[asDWORD(func)>>2], ppcArgsType ); + break; + } + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + { + // Add the object pointer as the last argument + ppcArgsType[argsCnt++] = ppcINTARG; + ppcArgsType[argsCnt] = ppcENDARG; + *((asQWORD*)pCurFixedArgValue) = (asPWORD)obj; + retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType ); + break; + } + default: + context->SetInternalException( TXT_INVALID_CALLING_CONVENTION ); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + else if( sysFunc->hostReturnSize == 1 ) + { + // Move the bits to the higher value to compensate for the adjustment that the caller does + retQW <<= 32; + } + + return retQW; +} + +END_AS_NAMESPACE + +#endif // AS_XENON +#endif // AS_MAX_PORTABILITY + + + diff --git a/3rdparty/angelscript/src/as_compiler.cpp b/3rdparty/angelscript/src/as_compiler.cpp new file mode 100644 index 0000000..f4e4f20 --- /dev/null +++ b/3rdparty/angelscript/src/as_compiler.cpp @@ -0,0 +1,15188 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_compiler.cpp +// +// The class that does the actual compilation of the functions +// + +#include // fmodf() pow() + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_compiler.h" +#include "as_tokendef.h" +#include "as_tokenizer.h" +#include "as_string_util.h" +#include "as_texts.h" +#include "as_parser.h" +#include "as_debug.h" +#include "as_context.h" // as_powi() + +BEGIN_AS_NAMESPACE + +// +// The calling convention rules for script functions: +// - If a class method returns a reference, the caller must guarantee the object pointer stays alive until the function returns, and the reference is no longer going to be used +// - If a class method doesn't return a reference, it must guarantee by itself that the this pointer stays alive during the function call. If no outside access is made, then the function is guaranteed to stay alive and nothing needs to be done +// - The object pointer is always passed as the first argument, position 0 +// - If the function returns a value type the caller must reserve the memory for this and pass the pointer as the first argument after the object pointer +// + + + + + +// TODO: I must correct the interpretation of a reference to objects in the compiler. +// A reference should mean that a pointer to the object is on the stack. +// No expression should end up as non-references to objects, as the actual object is +// never put on the stack. +// Local variables are declared as non-references, but the expression should be a reference to the variable. +// Function parameters of called functions can also be non-references, but in that case it means the +// object will be passed by value (currently on the heap, which will be moved to the application stack). +// +// The compiler shouldn't use the asCDataType::IsReference. The datatype should always be stored as non-references. +// Instead the compiler should keep track of references in TypeInfo, where it should also state how the reference +// is currently stored, i.e. in variable, in register, on stack, etc. + +asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine) +{ + builder = 0; + script = 0; + + variables = 0; + isProcessingDeferredParams = false; + isCompilingDefaultArg = false; + noCodeOutput = 0; +} + +asCCompiler::~asCCompiler() +{ + while( variables ) + { + asCVariableScope *var = variables; + variables = variables->parent; + + asDELETE(var,asCVariableScope); + } +} + +void asCCompiler::Reset(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptFunction *in_outFunc) +{ + this->builder = in_builder; + this->engine = in_builder->engine; + this->script = in_script; + this->outFunc = in_outFunc; + + hasCompileErrors = false; + + m_isConstructor = false; + m_isConstructorCalled = false; + m_classDecl = 0; + m_globalVar = 0; + + nextLabel = 0; + breakLabels.SetLength(0); + continueLabels.SetLength(0); + + numLambdas = 0; + + byteCode.ClearAll(); +} + +int asCCompiler::CompileDefaultConstructor(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptNode *in_node, asCScriptFunction *in_outFunc, sClassDeclaration *in_classDecl) +{ + Reset(in_builder, in_script, in_outFunc); + + m_classDecl = in_classDecl; + + // Insert a JitEntry at the start of the function for JIT compilers + byteCode.InstrPTR(asBC_JitEntry, 0); + + // Add a variable scope that might be needed to declare dummy variables + // in case the member initialization refers to undefined symbols. + AddVariableScope(); + + // Initialize the class members that have no explicit expression first. This will allow the + // base class' constructor to access these members without worry they will be uninitialized. + // This can happen if the base class' constructor calls a method that is overridden by the derived class + CompileMemberInitialization(&byteCode, true); + + // If the class is derived from another, then the base class' default constructor must be called + if( outFunc->objectType->derivedFrom ) + { + // Make sure the base class really has a default constructor + if( outFunc->objectType->derivedFrom->beh.construct == 0 ) + Error(TEXT_BASE_DOESNT_HAVE_DEF_CONSTR, in_node); + + // Call the base class' default constructor + byteCode.InstrSHORT(asBC_PSF, 0); + byteCode.Instr(asBC_RDSPtr); + byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE); + } + + // Initialize the class members that explicit expressions afterwards. This allow the expressions + // to access the base class members without worry they will be uninitialized + CompileMemberInitialization(&byteCode, false); + byteCode.OptimizeLocally(tempVariableOffsets); + + // If there are compile errors, there is no reason to build the final code + if( hasCompileErrors ) + return -1; + + // Pop the object pointer from the stack + byteCode.Ret(AS_PTR_SIZE); + + // Count total variable size + int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1; + outFunc->scriptData->variableSpace = varSize; + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__defconstr.txt").AddressOf(), in_outFunc); +#endif + + return 0; +} + +int asCCompiler::CompileFactory(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptFunction *in_outFunc) +{ + Reset(in_builder, in_script, in_outFunc); + + // Insert a JitEntry at the start of the function for JIT compilers + byteCode.InstrPTR(asBC_JitEntry, 0); + + // Find the corresponding constructor + asCDataType dt = asCDataType::CreateType(outFunc->returnType.GetTypeInfo(), false); + int constructor = 0; + for( unsigned int n = 0; n < dt.GetBehaviour()->factories.GetLength(); n++ ) + { + if( dt.GetBehaviour()->factories[n] == outFunc->id ) + { + constructor = dt.GetBehaviour()->constructors[n]; + break; + } + } + + // Allocate the class and instantiate it with the constructor + int varOffset = AllocateVariable(dt, true); + + outFunc->scriptData->variableSpace = AS_PTR_SIZE; + byteCode.InstrSHORT(asBC_PSF, (short)varOffset); + + // Copy all arguments to the top of the stack + // TODO: runtime optimize: Might be interesting to have a specific instruction for copying all arguments + int offset = (int)outFunc->GetSpaceNeededForArguments(); + for( int a = int(outFunc->parameterTypes.GetLength()) - 1; a >= 0; a-- ) + { + if( !outFunc->parameterTypes[a].IsPrimitive() || + outFunc->parameterTypes[a].IsReference() ) + { + offset -= AS_PTR_SIZE; + byteCode.InstrSHORT(asBC_PshVPtr, short(-offset)); + } + else + { + if( outFunc->parameterTypes[a].GetSizeOnStackDWords() == 2 ) + { + offset -= 2; + byteCode.InstrSHORT(asBC_PshV8, short(-offset)); + } + else + { + offset -= 1; + byteCode.InstrSHORT(asBC_PshV4, short(-offset)); + } + } + } + + int argDwords = (int)outFunc->GetSpaceNeededForArguments(); + byteCode.Alloc(asBC_ALLOC, dt.GetTypeInfo(), constructor, argDwords + AS_PTR_SIZE); + + // Return a handle to the newly created object + byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset); + + byteCode.Ret(argDwords); + + FinalizeFunction(); + + // Tell the virtual machine not to clean up parameters on exception + outFunc->dontCleanUpOnException = true; + +/* +#ifdef AS_DEBUG + // DEBUG: output byte code + asCString args; + args.Format("%d", outFunc->parameterTypes.GetLength()); + byteCode.DebugOutput(("__" + outFunc->name + "__factory" + args + ".txt").AddressOf(), engine); +#endif +*/ + return 0; +} + +void asCCompiler::FinalizeFunction() +{ + TimeIt("asCCompiler::FinalizeFunction"); + + asASSERT( outFunc->scriptData ); + asUINT n; + + // Finalize the bytecode + byteCode.Finalize(tempVariableOffsets); + + byteCode.ExtractObjectVariableInfo(outFunc); + + // Compile the list of object variables for the exception handler + // Start with the variables allocated on the heap, and then the ones allocated on the stack + for( n = 0; n < variableAllocations.GetLength(); n++ ) + { + if( (variableAllocations[n].IsObject() || variableAllocations[n].IsFuncdef()) && !variableAllocations[n].IsReference() ) + { + if( variableIsOnHeap[n] ) + { + outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetTypeInfo()); + outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n)); + } + } + } + outFunc->scriptData->objVariablesOnHeap = asUINT(outFunc->scriptData->objVariablePos.GetLength()); + for( n = 0; n < variableAllocations.GetLength(); n++ ) + { + if( (variableAllocations[n].IsObject() || variableAllocations[n].IsFuncdef()) && !variableAllocations[n].IsReference() ) + { + if( !variableIsOnHeap[n] ) + { + outFunc->scriptData->objVariableTypes.PushLast(variableAllocations[n].GetTypeInfo()); + outFunc->scriptData->objVariablePos.PushLast(GetVariableOffset(n)); + } + } + } + + // Copy byte code to the function + asASSERT( outFunc->scriptData->byteCode.GetLength() == 0 ); + outFunc->scriptData->byteCode.SetLength(byteCode.GetSize()); + byteCode.Output(outFunc->scriptData->byteCode.AddressOf()); + outFunc->AddReferences(); + outFunc->scriptData->stackNeeded = byteCode.largestStackUsed + outFunc->scriptData->variableSpace; + outFunc->scriptData->lineNumbers = byteCode.lineNumbers; + + // Extract the script section indexes too if there are any entries that are different from the function's script section + int lastIdx = outFunc->scriptData->scriptSectionIdx; + for( n = 0; n < byteCode.sectionIdxs.GetLength(); n++ ) + { + if( byteCode.sectionIdxs[n] != lastIdx ) + { + lastIdx = byteCode.sectionIdxs[n]; + outFunc->scriptData->sectionIdxs.PushLast(byteCode.lineNumbers[n*2]); + outFunc->scriptData->sectionIdxs.PushLast(lastIdx); + } + } +} + +// internal +int asCCompiler::SetupParametersAndReturnVariable(asCArray ¶meterNames, asCScriptNode *func) +{ + int stackPos = 0; + + if( outFunc->objectType ) + stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object + + // Add the first variable scope, which the parameters and + // variables declared in the outermost statement block is + // part of. + AddVariableScope(); + + bool isDestructor = false; + asCDataType returnType; + + // Examine return type + returnType = outFunc->returnType; + + // Check if this is a constructor or destructor + if( returnType.GetTokenType() == ttVoid && outFunc->objectType ) + { + if( outFunc->name[0] == '~' ) + isDestructor = true; + else if( outFunc->objectType->name == outFunc->name ) + m_isConstructor = true; + } + + // Is the return type allowed? + if( returnType != asCDataType::CreatePrimitive(ttVoid, false) && + !returnType.CanBeInstantiated() ) + { + // TODO: Hasn't this been validated by the builder already? + asCString str; + str.Format(TXT_RETURN_CANT_BE_s, returnType.Format(outFunc->nameSpace).AddressOf()); + Error(str, func); + } + + // If the return type is a value type returned by value the address of the + // location where the value will be stored is pushed on the stack before + // the arguments + if( !(isDestructor || m_isConstructor) && outFunc->DoesReturnOnStack() ) + stackPos -= AS_PTR_SIZE; + + asCVariableScope vs(0); + + // Declare parameters + asUINT n; + for( n = 0; n < parameterNames.GetLength(); n++ ) + { + // Get the parameter type + asCDataType &type = outFunc->parameterTypes[n]; + asETypeModifiers inoutFlag = n < outFunc->inOutFlags.GetLength() ? outFunc->inOutFlags[n] : asTM_NONE; + + // Is the data type allowed? + // TODO: Hasn't this been validated by the builder already? + if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstantiated()) || + (!type.IsReference() && !type.CanBeInstantiated()) ) + { + asCString parm = type.Format(outFunc->nameSpace); + if( inoutFlag == asTM_INREF ) + parm += "in"; + else if( inoutFlag == asTM_OUTREF ) + parm += "out"; + + asCString str; + str.Format(TXT_PARAMETER_CANT_BE_s, parm.AddressOf()); + Error(str, func); + } + + // If the parameter has a name then declare it as variable + if( parameterNames[n] != "" ) + { + asCString &name = parameterNames[n]; + if( vs.DeclareVariable(name.AddressOf(), type, stackPos, true) < 0 ) + { + // TODO: It might be an out-of-memory too + Error(TXT_PARAMETER_ALREADY_DECLARED, func); + } + + // Add marker for variable declaration + byteCode.VarDecl((int)outFunc->scriptData->variables.GetLength()); + outFunc->AddVariable(name, type, stackPos); + } + else + vs.DeclareVariable("", type, stackPos, true); + + // Move to next parameter + stackPos -= type.GetSizeOnStackDWords(); + } + + for( n = asUINT(vs.variables.GetLength()); n-- > 0; ) + variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset, vs.variables[n]->onHeap); + + variables->DeclareVariable("return", returnType, stackPos, true); + + return stackPos; +} + +void asCCompiler::CompileMemberInitialization(asCByteCode *bc, bool onlyDefaults) +{ + asASSERT( m_classDecl ); + + // Initialize each member in the order they were declared + for( asUINT n = 0; n < outFunc->objectType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = outFunc->objectType->properties[n]; + + // Check if the property has an initialization expression + asCScriptNode *declNode = 0; + asCScriptNode *initNode = 0; + asCScriptCode *initScript = 0; + for( asUINT m = 0; m < m_classDecl->propInits.GetLength(); m++ ) + { + if( m_classDecl->propInits[m].name == prop->name ) + { + declNode = m_classDecl->propInits[m].declNode; + initNode = m_classDecl->propInits[m].initNode; + initScript = m_classDecl->propInits[m].file; + break; + } + } + + // If declNode is null, the property was inherited in which case + // it was already initialized by the base class' constructor + if( declNode ) + { + if( initNode ) + { + if( onlyDefaults ) + continue; + +#ifdef AS_NO_MEMBER_INIT + // Give an error as the initialization in the declaration has been disabled + asCScriptCode *origScript = script; + script = initScript; + Error("Initialization of members in declaration is not supported", initNode); + script = origScript; + + // Clear the initialization node + initNode = 0; + initScript = script; +#else + // Re-parse the initialization expression as the parser now knows the types, which it didn't earlier + asCParser parser(builder); + int r = parser.ParseVarInit(initScript, initNode); + if( r < 0 ) + continue; + + initNode = parser.GetScriptNode(); +#endif + } + else + { + if( !onlyDefaults ) + continue; + } + +#ifdef AS_NO_MEMBER_INIT + // The initialization will be done in the asCScriptObject constructor, so + // here we should just validate that the member has a default constructor + if( prop->type.IsObject() && + !prop->type.IsObjectHandle() && + (((prop->type.GetTypeInfo()->flags & asOBJ_REF) && + prop->type.GetBehaviour()->factory == 0) || + ((prop->type.GetTypeInfo()->flags & asOBJ_VALUE) && + prop->type.GetBehaviour()->construct == 0 && + !(prop->type.GetTypeInfo()->flags & asOBJ_POD))) ) + { + // Class has no default factory/constructor. + asCString str; + // TODO: funcdef: asCDataType should have a GetTypeName() + if( prop->type.GetFuncDef() ) + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, prop->type.GetFuncDef()->GetName()); + else + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, prop->type.GetTypeInfo()->GetName()); + Error(str, declNode); + } +#else + // Temporarily set the script that is being compiled to where the member initialization is declared. + // The script can be different when including mixin classes from a different script section + asCScriptCode *origScript = script; + script = initScript; + + // Add a line instruction with the position of the declaration + LineInstr(bc, declNode->tokenPos); + + // Compile the initialization + asQWORD constantValue; + asCByteCode bcInit(engine); + CompileInitialization(initNode, &bcInit, prop->type, declNode, prop->byteOffset, &constantValue, 2); + bcInit.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&bcInit); + + script = origScript; +#endif + } + } +} + +// Entry +int asCCompiler::CompileFunction(asCBuilder *in_builder, asCScriptCode *in_script, asCArray &in_parameterNames, asCScriptNode *in_func, asCScriptFunction *in_outFunc, sClassDeclaration *in_classDecl) +{ + TimeIt("asCCompiler::CompileFunction"); + + Reset(in_builder, in_script, in_outFunc); + int buildErrors = builder->numErrors; + + int stackPos = SetupParametersAndReturnVariable(in_parameterNames, in_func); + + //-------------------------------------------- + // Compile the statement block + + if( m_isConstructor ) + m_classDecl = in_classDecl; + + // We need to parse the statement block now + asCScriptNode *blockBegin; + + // If the function signature was implicit, e.g. virtual property accessor or + // lambda function, then the received node already is the statement block + if( in_func->nodeType != snStatementBlock ) + blockBegin = in_func->lastChild; + else + blockBegin = in_func; + + // TODO: memory: We can parse the statement block one statement at a time, thus save even more memory + // TODO: optimize: For large functions, the parsing of the statement block can take a long time. Presumably because a lot of memory needs to be allocated + asCParser parser(builder); + int r = parser.ParseStatementBlock(script, blockBegin); + if( r < 0 ) return -1; + asCScriptNode *block = parser.GetScriptNode(); + + // Reserve a label for the cleanup code + nextLabel++; + + bool hasReturn; + asCByteCode bc(engine); + LineInstr(&bc, blockBegin->tokenPos); + CompileStatementBlock(block, false, &hasReturn, &bc); + LineInstr(&bc, blockBegin->tokenPos + blockBegin->tokenLength); + + // Make sure there is a return in all paths (if not return type is void) + // Don't bother with this check if there are compiler errors, e.g. Unreachable code + if( !hasCompileErrors && outFunc->returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + { + if( hasReturn == false ) + Error(TXT_NOT_ALL_PATHS_RETURN, blockBegin); + } + + //------------------------------------------------ + // Concatenate the bytecode + + // Insert a JitEntry at the start of the function for JIT compilers + byteCode.InstrPTR(asBC_JitEntry, 0); + + if( outFunc->objectType ) + { + if( m_isConstructor ) + { + if( outFunc->objectType->derivedFrom ) + { + // Call the base class' default constructor unless called manually in the code + if( !m_isConstructorCalled ) + { + if( outFunc->objectType->derivedFrom->beh.construct ) + { + // Initialize members without explicit expression first + CompileMemberInitialization(&byteCode, true); + + // Call base class' constructor + asCByteCode tmpBC(engine); + tmpBC.InstrSHORT(asBC_PSF, 0); + tmpBC.Instr(asBC_RDSPtr); + tmpBC.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE); + tmpBC.OptimizeLocally(tempVariableOffsets); + byteCode.AddCode(&tmpBC); + + // Add the initialization of the members with explicit expressions + CompileMemberInitialization(&byteCode, false); + } + else + Error(TEXT_BASE_DOESNT_HAVE_DEF_CONSTR, blockBegin); + } + else + { + // Only initialize members that don't have an explicit expression + // The members that are explicitly initialized will be initialized after the call to base class' constructor + CompileMemberInitialization(&byteCode, true); + } + } + else + { + // Add the initialization of the members + CompileMemberInitialization(&byteCode, true); + CompileMemberInitialization(&byteCode, false); + } + } + } + + // Add the code for the statement block + byteCode.AddCode(&bc); + + // Count total variable size + int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1; + outFunc->scriptData->variableSpace = varSize; + + // Deallocate all local variables + int n; + for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + if( v->stackOffset > 0 ) + { + // Call variables destructors + if( v->name != "return" && v->name != "return address" ) + CallDestructor(v->type, v->stackOffset, v->onHeap, &byteCode); + + DeallocateVariable(v->stackOffset); + } + } + + // This is the label that return statements jump to + // in order to exit the function + byteCode.Label(0); + + // Call destructors for function parameters + for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + if( v->stackOffset <= 0 ) + { + // Call variable destructors here, for variables not yet destroyed + if( v->name != "return" && v->name != "return address" ) + CallDestructor(v->type, v->stackOffset, v->onHeap, &byteCode); + } + + // Do not deallocate parameters + } + + // Check if the number of labels in the functions isn't too many to be handled + if( nextLabel >= (1<<15) ) + Error(TXT_TOO_MANY_JUMP_LABELS, in_func); + + // If there are compile errors, there is no reason to build the final code + if( hasCompileErrors || builder->numErrors != buildErrors ) + return -1; + + // At this point there should be no variables allocated + asASSERT(variableAllocations.GetLength() == freeVariables.GetLength()); + + // Remove the variable scope + RemoveVariableScope(); + + byteCode.Ret(-stackPos); + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + if( outFunc->objectType ) + byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), in_outFunc); + else + byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), in_outFunc); +#endif + + return 0; +} + +int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool isGlobalVar, bool derefDest) +{ + if( !type.IsObject() ) + return 0; + + // CallCopyConstructor should not be called for object handles. + asASSERT( !type.IsObjectHandle() ); + + asCArray args; + args.PushLast(arg); + + // The reference parameter must be pushed on the stack + asASSERT( arg->type.dataType.GetTypeInfo() == type.GetTypeInfo() ); + + // Since we're calling the copy constructor, we have to trust the function to not do + // anything stupid otherwise we will just enter a loop, as we try to make temporary + // copies of the argument in order to guarantee safety. + + + if( type.GetTypeInfo()->flags & asOBJ_REF ) + { + asCExprContext ctx(engine); + + int func = 0; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) func = beh->copyfactory; + + if( func > 0 ) + { + if( !isGlobalVar ) + { + // Call factory and store the handle in the given variable + PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()), true, offset); + + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); + } + else + { + // Call factory + PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo())); + + // Store the returned handle in the global variable + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo()); + ctx.bc.Instr(asBC_PopPtr); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + bc->AddCode(&ctx.bc); + + return 0; + } + } + else + { + asSTypeBehaviour *beh = type.GetBehaviour(); + int func = beh ? beh->copyconstruct : 0; + if( func > 0 ) + { + // Push the address where the object will be stored on the stack, before the argument + // TODO: When the context is serializable this probably has to be changed, since this + // pointer can remain on the stack while the context is suspended. There is no + // risk the pointer becomes invalid though, there is just no easy way to serialize it. + asCByteCode tmp(engine); + if( isGlobalVar ) + tmp.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + else if( isObjectOnHeap ) + tmp.InstrSHORT(asBC_PSF, (short)offset); + tmp.AddCode(bc); + bc->AddCode(&tmp); + + // When the object is allocated on the stack the object pointer + // must be pushed on the stack after the arguments + if( !isObjectOnHeap ) + { + asASSERT( !isGlobalVar ); + bc->InstrSHORT(asBC_PSF, (short)offset); + if( derefDest ) + { + // The variable is a reference to the real location, so we need to dereference it + bc->Instr(asBC_RDSPtr); + } + } + + asCExprContext ctx(engine); + PerformFunctionCall(func, &ctx, isObjectOnHeap, &args, CastToObjectType(type.GetTypeInfo())); + + bc->AddCode(&ctx.bc); + + // TODO: value on stack: This probably needs to be done in PerformFunctionCall + // Mark the object as initialized + if( !isObjectOnHeap ) + bc->ObjInfo(offset, asOBJ_INIT); + + + return 0; + } + } + + // Class has no copy constructor/factory. + asCString str; + str.Format(TXT_NO_COPY_CONSTRUCTOR_FOR_s, type.GetTypeInfo()->GetName()); + Error(str, node); + + return -1; +} + +int asCCompiler::CallDefaultConstructor(const asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool derefDest) +{ + if( !type.IsObject() || type.IsObjectHandle() ) + return 0; + + if( type.GetTypeInfo()->flags & asOBJ_REF ) + { + asCExprContext ctx(engine); + ctx.exprNode = node; + + int func = 0; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) + { + func = beh->factory; + + // If no trivial default factory is found, look for a factory where all params have default args + if( func == 0 ) + { + for( asUINT n = 0; n < beh->factories.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[beh->factories[n]]; + if( f->defaultArgs.GetLength() == f->parameterTypes.GetLength() && + f->defaultArgs[0] != 0 ) + { + func = beh->factories[n]; + break; + } + } + } + } + + if( func > 0 ) + { + asCArray args; + asCScriptFunction *f = engine->scriptFunctions[func]; + if( f->parameterTypes.GetLength() ) + { + // Add the default values for arguments not explicitly supplied + CompileDefaultAndNamedArgs(node, args, func, CastToObjectType(type.GetTypeInfo())); + + PrepareFunctionCall(func, &ctx.bc, args); + + MoveArgsToStack(func, &ctx.bc, args, false); + } + + if( isVarGlobOrMem == 0 ) + { + // Call factory and store the handle in the given variable + PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo()), true, offset); + + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); + } + else + { + // Call factory + PerformFunctionCall(func, &ctx, false, &args, CastToObjectType(type.GetTypeInfo())); + + // TODO: runtime optimize: Should have a way of storing the object pointer directly to the destination + // instead of first storing it in a local variable and then copying it to the + // destination. + + if( !(type.GetTypeInfo()->flags & asOBJ_SCOPED) ) + { + // Only dereference the variable if not a scoped type + ctx.bc.Instr(asBC_RDSPtr); + } + + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the class member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + + if( type.GetTypeInfo()->flags & asOBJ_SCOPED ) + { + // For scoped typed we must move the reference from the local + // variable rather than copy it as there is no AddRef behaviour + ctx.bc.InstrSHORT_DW(asBC_COPY, AS_PTR_SIZE, asTYPEID_OBJHANDLE | engine->GetTypeIdFromDataType(type)); + + // Clear the local variable so the reference isn't released + ctx.bc.InstrSHORT(asBC_ClrVPtr, ctx.type.stackOffset); + } + else + { + if( type.IsFuncdef() ) + ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo()); + } + ctx.bc.Instr(asBC_PopPtr); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + bc->AddCode(&ctx.bc); + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + + return 0; + } + } + else + { + asCExprContext ctx(engine); + ctx.exprNode = node; + + asSTypeBehaviour *beh = type.GetBehaviour(); + + int func = 0; + if( beh ) + { + func = beh->construct; + + // If no trivial default constructor is found, look for a constructor where all params have default args + if( func == 0 ) + { + for( asUINT n = 0; n < beh->constructors.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[beh->constructors[n]]; + if( f->defaultArgs.GetLength() == f->parameterTypes.GetLength() && + f->defaultArgs[0] != 0 ) + { + func = beh->constructors[n]; + break; + } + } + } + } + + // Allocate and initialize with the default constructor + if( func != 0 || (type.GetTypeInfo()->flags & asOBJ_POD) ) + { + asCArray args; + asCScriptFunction *f = engine->scriptFunctions[func]; + if( f && f->parameterTypes.GetLength() ) + { + // Add the default values for arguments not explicitly supplied + CompileDefaultAndNamedArgs(node, args, func, CastToObjectType(type.GetTypeInfo())); + + PrepareFunctionCall(func, &ctx.bc, args); + + MoveArgsToStack(func, &ctx.bc, args, false); + } + + if( !isObjectOnHeap ) + { + if( isVarGlobOrMem == 0 ) + { + // There is nothing to do if there is no function, + // as the memory is already allocated on the stack + if( func ) + { + // Call the constructor as a normal function + bc->InstrSHORT(asBC_PSF, (short)offset); + if( derefDest ) + bc->Instr(asBC_RDSPtr); + + asCExprContext ctxCall(engine); + PerformFunctionCall(func, &ctxCall, false, 0, CastToObjectType(type.GetTypeInfo())); + bc->AddCode(&ctxCall.bc); + + // TODO: value on stack: This probably needs to be done in PerformFunctionCall + // Mark the object as initialized + bc->ObjInfo(offset, asOBJ_INIT); + } + } + else if( isVarGlobOrMem == 2 ) + { + // Only POD types can be allocated inline in script classes + asASSERT( type.GetTypeInfo()->flags & asOBJ_POD ); + + if( func ) + { + // Call the constructor as a normal function + bc->InstrSHORT(asBC_PSF, 0); + bc->Instr(asBC_RDSPtr); + bc->InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + + asCExprContext ctxCall(engine); + PerformFunctionCall(func, &ctxCall, false, 0, CastToObjectType(type.GetTypeInfo())); + bc->AddCode(&ctxCall.bc); + } + } + else + { + asASSERT( false ); + } + } + else + { + if( isVarGlobOrMem == 0 ) + bc->InstrSHORT(asBC_PSF, (short)offset); + else if( isVarGlobOrMem == 1 ) + bc->InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + else + { + bc->InstrSHORT(asBC_PSF, 0); + bc->Instr(asBC_RDSPtr); + bc->InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + + if( (type.GetTypeInfo()->flags & asOBJ_TEMPLATE) ) + { + asCScriptFunction *descr = engine->scriptFunctions[func]; + asASSERT( descr->funcType == asFUNC_SCRIPT ); + + // Find the id of the real constructor and not the generated stub + asUINT id = 0; + asDWORD *funcBc = descr->scriptData->byteCode.AddressOf(); + while( funcBc ) + { + if( (*(asBYTE*)funcBc) == asBC_CALLSYS ) + { + id = asBC_INTARG(funcBc); + break; + } + funcBc += asBCTypeSize[asBCInfo[*(asBYTE*)funcBc].type]; + } + + asASSERT( id ); + + bc->InstrPTR(asBC_OBJTYPE, type.GetTypeInfo()); + bc->Alloc(asBC_ALLOC, type.GetTypeInfo(), id, AS_PTR_SIZE + AS_PTR_SIZE); + } + else + bc->Alloc(asBC_ALLOC, type.GetTypeInfo(), func, AS_PTR_SIZE); + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + + return 0; + } + } + + // Class has no default factory/constructor. + asCString str; + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetTypeInfo()->GetName()); + Error(str, node); + + return -1; +} + +void asCCompiler::CallDestructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc) +{ + if( !type.IsReference() ) + { + // Call destructor for the data type + if( type.IsObject() || type.IsFuncdef() ) + { + // The null pointer doesn't need to be destroyed + if( type.IsNullHandle() ) + return; + + // Nothing is done for list pattern types, as this is taken care of by the CompileInitList method + if( type.GetTypeInfo()->flags & asOBJ_LIST_PATTERN ) + return; + + if( isObjectOnHeap || type.IsObjectHandle() ) + { + // Free the memory + if (type.IsFuncdef()) + bc->InstrW_PTR(asBC_FREE, (short)offset, &engine->functionBehaviours); + else + bc->InstrW_PTR(asBC_FREE, (short)offset, type.GetTypeInfo()); + } + else + { + asASSERT( type.GetTypeInfo()->GetFlags() & asOBJ_VALUE ); + + if( type.GetBehaviour()->destruct ) + { + // Call the destructor as a regular function + asCExprContext ctx(engine); + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + PerformFunctionCall(type.GetBehaviour()->destruct, &ctx); + ctx.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&ctx.bc); + } + + // TODO: Value on stack: This probably needs to be done in PerformFunctionCall + // Mark the object as destroyed + bc->ObjInfo(offset, asOBJ_UNINIT); + } + } + } +} + +void asCCompiler::LineInstr(asCByteCode *bc, size_t pos) +{ + int r, c; + script->ConvertPosToRowCol(pos, &r, &c); + bc->Line(r, c, script->idx); +} + +void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc) +{ + *hasReturn = false; + bool isFinished = false; + bool hasUnreachableCode = false; + bool hasReturnBefore = false; + + if( ownVariableScope ) + { + bc->Block(true); + AddVariableScope(); + } + + asCScriptNode *node = block->firstChild; + while( node ) + { +#ifdef AS_DEBUG + // Keep the current line in a variable so it will be easier + // to determine where in a script an assert is occurring. + int currentLine = 0; + script->ConvertPosToRowCol(node->tokenPos, ¤tLine, 0); +#endif + + if( !hasUnreachableCode && (*hasReturn || isFinished) ) + { + // Empty statements don't count + if( node->nodeType != snExpressionStatement || node->firstChild ) + { + hasUnreachableCode = true; + Warning(TXT_UNREACHABLE_CODE, node); + } + + if( *hasReturn ) + hasReturnBefore = true; + } + + if( node->nodeType == snBreak || node->nodeType == snContinue ) + isFinished = true; + + asCByteCode statement(engine); + if( node->nodeType == snDeclaration ) + CompileDeclaration(node, &statement); + else + CompileStatement(node, hasReturn, &statement); + + // Ignore missing returns in unreachable code paths + if( !(*hasReturn) && hasReturnBefore ) + *hasReturn = true; + + LineInstr(bc, node->tokenPos); + bc->AddCode(&statement); + + if( !hasCompileErrors ) + { + asASSERT( tempVariables.GetLength() == 0 ); + asASSERT( reservedVariables.GetLength() == 0 ); + } + + node = node->next; + } + + if( ownVariableScope ) + { + // Deallocate variables in this block, in reverse order + for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + // If the block is terminated with a break, continue, or + // return the variables are already destroyed + if( !isFinished && !*hasReturn ) + CallDestructor(v->type, v->stackOffset, v->onHeap, bc); + + // Don't deallocate function parameters + if( v->stackOffset > 0 ) + DeallocateVariable(v->stackOffset); + } + + RemoveVariableScope(); + bc->Block(false); + } +} + +// Entry +int asCCompiler::CompileGlobalVariable(asCBuilder *in_builder, asCScriptCode *in_script, asCScriptNode *in_node, sGlobalVariableDescription *in_gvar, asCScriptFunction *in_outFunc) +{ + Reset(in_builder, in_script, in_outFunc); + m_globalVar = in_gvar; + + // Add a variable scope (even though variables can't be declared) + AddVariableScope(); + + in_gvar->isPureConstant = false; + + // Parse the initialization nodes + asCParser parser(builder); + if (in_node) + { + int r = parser.ParseVarInit(in_script, in_node); + if (r < 0) + return r; + + in_node = parser.GetScriptNode(); + } + + asCExprContext compiledCtx(engine); + bool preCompiled = false; + if (in_gvar->datatype.IsAuto()) + { + preCompiled = CompileAutoType(in_gvar->datatype, compiledCtx, in_node, in_gvar->declaredAtNode); + if (!preCompiled) + { + // If it wasn't possible to determine the type from the expression then there + // is no need to continue with the initialization. The error was already reported + // in CompileAutoType. + return -1; + } + } + if( in_gvar->property == 0 ) + { + in_gvar->property = builder->module->AllocateGlobalProperty(in_gvar->name.AddressOf(), in_gvar->datatype, in_gvar->ns); + in_gvar->index = in_gvar->property->id; + } + + // Compile the expression + asCExprContext ctx(engine); + asQWORD constantValue = 0; + if( CompileInitialization(in_node, &ctx.bc, in_gvar->datatype, in_gvar->declaredAtNode, in_gvar->index, &constantValue, 1, preCompiled ? &compiledCtx : 0) ) + { + // Should the variable be marked as pure constant? + if( in_gvar->datatype.IsPrimitive() && in_gvar->datatype.IsReadOnly() ) + { + in_gvar->isPureConstant = true; + in_gvar->constantValue = constantValue; + } + } + + // Concatenate the bytecode + int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1; + + // Add information on the line number for the global variable + size_t pos = 0; + if( in_gvar->declaredAtNode ) + pos = in_gvar->declaredAtNode->tokenPos; + else if( in_gvar->initializationNode ) + pos = in_gvar->initializationNode->tokenPos; + LineInstr(&byteCode, pos); + + // Reserve space for all local variables + outFunc->scriptData->variableSpace = varSize; + + ctx.bc.OptimizeLocally(tempVariableOffsets); + + byteCode.AddCode(&ctx.bc); + + // Deallocate variables in this block, in reverse order + for( int n = (int)variables->variables.GetLength() - 1; n >= 0; --n ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + CallDestructor(v->type, v->stackOffset, v->onHeap, &byteCode); + + DeallocateVariable(v->stackOffset); + } + + if( hasCompileErrors ) return -1; + + // At this point there should be no variables allocated + asASSERT(variableAllocations.GetLength() == freeVariables.GetLength()); + + // Remove the variable scope again + RemoveVariableScope(); + + byteCode.Ret(0); + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + byteCode.DebugOutput(("___init_" + in_gvar->name + ".txt").AddressOf(), outFunc); +#endif + + return 0; +} + +void asCCompiler::DetermineSingleFunc(asCExprContext *ctx, asCScriptNode *node) +{ + // Don't do anything if this is not a deferred global function + if( !ctx->IsGlobalFunc() ) + return; + + // Determine the namespace + asSNameSpace *ns = 0; + asCString name = ""; + int pos = ctx->methodName.FindLast("::"); + if( pos >= 0 ) + { + asCString nsName = ctx->methodName.SubString(0, pos+2); + + // Cut off the :: + if( nsName.GetLength() > 2 ) + nsName.SetLength(nsName.GetLength()-2); + + ns = DetermineNameSpace(nsName); + name = ctx->methodName.SubString(pos+2); + } + else + { + DetermineNameSpace(""); + name = ctx->methodName; + } + + asCArray funcs; + if( ns ) + builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); + + // CompileVariableAccess should guarantee that at least one function is exists + asASSERT( funcs.GetLength() > 0 ); + + if( funcs.GetLength() > 1 ) + { + asCString str; + str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, ctx->methodName.AddressOf()); + Error(str, node); + + // Fall through so the compiler can continue as if only one function was matching + } + + // A shared object may not access global functions unless they too are shared (e.g. registered functions) + if( !builder->GetFunctionDescription(funcs[0])->IsShared() && + outFunc->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, builder->GetFunctionDescription(funcs[0])->GetDeclaration()); + Error(msg, node); + + // Fall through so the compiler can continue anyway + } + + // Push the function pointer on the stack + ctx->bc.InstrPTR(asBC_FuncPtr, builder->GetFunctionDescription(funcs[0])); + ctx->type.Set(asCDataType::CreateType(engine->FindMatchingFuncdef(builder->GetFunctionDescription(funcs[0]), builder->module), false)); + ctx->type.dataType.MakeHandle(true); + ctx->type.isExplicitHandle = true; + ctx->methodName = ""; +} + +void asCCompiler::CompileInitAsCopy(asCDataType &dt, int offset, asCByteCode *bc, asCExprContext *arg, asCScriptNode *node, bool derefDestination) +{ + bool isObjectOnHeap = derefDestination ? false : IsVariableOnHeap(offset); + + // Use copy constructor if available. + if(CastToObjectType(dt.GetTypeInfo()) && CastToObjectType(dt.GetTypeInfo())->beh.copyconstruct ) + { + PrepareForAssignment(&dt, arg, node, true); + int r = CallCopyConstructor(dt, offset, isObjectOnHeap, bc, arg, node, 0, derefDestination); + if( r < 0 && tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + } + else + { + // TODO: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args + + // Allocate and construct the temporary object before whatever is already in the bytecode + asCByteCode tmpBC(engine); + int r = CallDefaultConstructor(dt, offset, isObjectOnHeap, &tmpBC, node, 0, derefDestination); + if( r < 0 ) + { + if( tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + return; + } + + tmpBC.AddCode(bc); + bc->AddCode(&tmpBC); + + // Assign the evaluated expression to the temporary variable + PrepareForAssignment(&dt, arg, node, true); + bc->AddCode(&arg->bc); + + // Call the opAssign method to assign the value to the temporary object + dt.MakeReference(isObjectOnHeap); + asCExprValue type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; + + if( dt.IsObjectHandle() ) + type.isExplicitHandle = true; + + bc->InstrSHORT(asBC_PSF, (short)offset); + if( derefDestination ) + bc->Instr(asBC_RDSPtr); + + r = PerformAssignment(&type, &arg->type, bc, node); + if( r < 0 ) + { + if( tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + return; + } + + // Pop the reference that was pushed on the stack if the result is an object + if( type.dataType.IsObject() || type.dataType.IsFuncdef() ) + bc->Instr(asBC_PopPtr); + + // If the assignment operator returned an object by value it will + // be in a temporary variable which we need to destroy now + if( type.isTemporary && type.stackOffset != (short)offset ) + ReleaseTemporaryVariable(type.stackOffset, bc); + + // Release the original value too in case it is a temporary + ReleaseTemporaryVariable(arg->type, bc); + } +} + +int asCCompiler::PrepareArgument(asCDataType *paramType, asCExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, bool isMakingCopy) +{ + asCDataType param = *paramType; + if( paramType->GetTokenType() == ttQuestion ) + { + // The function is expecting a var type. If the argument is a function name, we must now decide which function it is + DetermineSingleFunc(ctx, node); + + // Since the function is expecting a var type ?, then we don't want to convert the argument to anything else + param = ctx->type.dataType; + param.MakeHandle(ctx->type.isExplicitHandle || ctx->type.IsNullConstant()); + + // Treat the void expression like a null handle when working with var types + if( ctx->IsVoidExpression() ) + param = asCDataType::CreateNullHandle(); + + // If value assign is disabled for reference types, then make + // sure to always pass the handle to ? parameters + if( builder->engine->ep.disallowValueAssignForRefType && + ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED) ) + { + param.MakeHandle(true); + } + + param.MakeReference(paramType->IsReference()); + param.MakeReadOnly(paramType->IsReadOnly()); + } + else + param = *paramType; + + asCDataType dt = param; + + // Need to protect arguments by reference + if( isFunction && dt.IsReference() ) + { + // Allocate a temporary variable of the same type as the argument + dt.MakeReference(false); + dt.MakeReadOnly(false); + + int offset; + if( refType == asTM_INREF ) + { + ProcessPropertyGetAccessor(ctx, node); + + // Add the type id as hidden arg if the parameter is a ? type + if( paramType->GetTokenType() == ttQuestion ) + { + asCByteCode tmpBC(engine); + + // Place the type id on the stack as a hidden parameter + tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param)); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + } + + if( dt.IsPrimitive() ) + { + // If the reference is const, then it is not necessary to make a copy if the value already is a variable + // Even if the same variable is passed in another argument as non-const then there is no problem + IsVariableInitialized(&ctx->type, node); + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true); + + if( !(param.IsReadOnly() && ctx->type.isVariable) ) + ConvertToTempVariable(ctx); + + PushVariableOnStack(ctx, true); + ctx->type.dataType.MakeReadOnly(param.IsReadOnly()); + } + else if( ctx->type.dataType.IsNullHandle() ) + { + // Make sure the argument type can support handles (or is itself a handle) + if( !dt.SupportHandles() && !dt.IsObjectHandle() ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.Set(param); + return -1; + } + + // Need to initialize a local temporary variable to + // represent the null handle when passed as reference + asASSERT( ctx->bc.GetLastInstr() == asBC_PshNull ); + ctx->bc.Instr(asBC_PopPtr); + + dt.MakeHandle(true); + offset = AllocateVariableNotIn(dt, true, false, ctx); + + // Push the reference to the variable on the stack + ctx->bc.InstrWORD(asBC_PSF, (short)offset); + + ctx->type.SetVariable(dt, offset, true); + } + else + { + IsVariableInitialized(&ctx->type, node); + + if( !isMakingCopy ) + { + // Even though the parameter expects a reference, it is only meant to be + // used as input value and doesn't have to refer to the actual object, so it + // is OK to do an implicit conversion. + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true); + if( !ctx->type.dataType.IsEqualExceptRefAndConst(param) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.Set(param); + return -1; + } + + // The compiler must guarantee that the object stays alive during the execution + // of the function, and it must also guarantee that the value isn't modified by + // the function. + + // If the argument is a temporary local variable then it is safe to be passed to + // the function as it is, since the local variable will stay alive, and since it + // is temporary there is no side effect if the function modifies it. + + // If the parameter is read-only and therefore guaranteed not to be modified by the + // function, then it is enough that the variable is local to guarantee the lifetime. + if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable) ) + { + if( ctx->type.dataType.IsFuncdef() || ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && param.IsReadOnly() && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED)) ) + { + // Funcdefs only need an extra handle to guarantee the lifetime. + + // If the object is a reference type (except scoped reference types), and the + // parameter is a const reference, then it is not necessary to make a copy of the + // object. The compiler just needs to hold a handle to guarantee the lifetime. + + // Allocate a handle variable + dt.MakeHandle(true); + offset = AllocateVariableNotIn(dt, true, false, ctx); + + // Copy the handle + Dereference(ctx, true); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + if (ctx->type.dataType.IsFuncdef()) + ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo()); + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + + // The type should be set to the param type instead of dt to guarantee + // that the expression keeps the correct type for variable ? args. Otherwise + // MoveArgsToStack will use the wrong bytecode to move the arg to the stack + ctx->type.SetVariable(param, offset, true); + } + else + { + // Make a copy of the object to guarantee that the original isn't modified + asASSERT(!dt.IsFuncdef()); + + // Allocate and initialize a temporary local object + offset = AllocateVariableNotIn(dt, true, false, ctx); + CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false); + + // Push the object pointer on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPtr); + + // Set the resulting type + ctx->type.Set(dt); + ctx->type.isTemporary = true; + ctx->type.stackOffset = short(offset); + if( dt.IsObjectHandle() ) + ctx->type.isExplicitHandle = true; + ctx->type.dataType.MakeReference(false); + if( paramType->IsReadOnly() ) + ctx->type.dataType.MakeReadOnly(true); + } + } + } + else + { + // We must guarantee that the address to the value is on the stack + if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && + !ctx->type.dataType.IsObjectHandle() && + ctx->type.dataType.IsReference() ) + Dereference(ctx, true); + } + } + } + else if( refType == asTM_OUTREF ) + { + // Add the type id as hidden arg if the parameter is a ? type + if( paramType->GetTokenType() == ttQuestion ) + { + asCByteCode tmpBC(engine); + + // Place the type id on the stack as a hidden parameter + tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param)); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + } + + // If the expression is marked as clean, then it can be used directly + // without the need to allocate another temporary value as it is known + // that the argument has no other value than the default + if( ctx->isCleanArg ) + { + // Must be a local variable + asASSERT( ctx->type.isVariable ); + } + else + { + // Make sure the variable is not used in the expression + offset = AllocateVariableNotIn(dt, true, false, ctx); + + if( dt.IsPrimitive() ) + { + ctx->type.SetVariable(dt, offset, true); + PushVariableOnStack(ctx, true); + } + else + { + // TODO: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args + + // Allocate and construct the temporary object + asCByteCode tmpBC(engine); + CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + + dt.MakeReference(!(dt.IsObject() || dt.IsFuncdef()) || dt.IsObjectHandle()); + asCExprValue type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; + + ctx->type = type; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPtr); + } + + // After the function returns the temporary variable will + // be assigned to the expression, if it is a valid lvalue + } + } + else if( refType == asTM_INOUTREF ) + { + ProcessPropertyGetAccessor(ctx, node); + + // Add the type id as hidden arg if the parameter is a ? type + if( paramType->GetTokenType() == ttQuestion ) + { + asCByteCode tmpBC(engine); + + // Place the type id on the stack as a hidden parameter + tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param)); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + } + + // Literal constants cannot be passed to inout ref arguments + if( !ctx->type.isVariable && ctx->type.isConstant ) + { + // Unless unsafe references are turned on and the reference is const + if( param.IsReadOnly() && engine->ep.allowUnsafeReferences ) + { + // Since the parameter is a const & make a copy. + ConvertToTempVariable(ctx); + ctx->type.dataType.MakeReadOnly(true); + } + else + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + } + + // Perform implicit ref cast if necessary, but don't allow the implicit conversion to create new objects + if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && ctx->type.dataType.GetTypeInfo() != dt.GetTypeInfo() ) + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, false); + + // Only objects that support object handles + // can be guaranteed to be safe. Local variables are + // already safe, so there is no need to add an extra + // references + if( !engine->ep.allowUnsafeReferences && + !ctx->type.isVariable && + (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && + !ctx->type.dataType.IsObjectHandle() && + ((ctx->type.dataType.GetBehaviour()->addref && + ctx->type.dataType.GetBehaviour()->release) || + (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOCOUNT) || + ctx->type.dataType.IsFuncdef()) ) + { + // Store a handle to the object as local variable + asCExprContext tmp(engine); + dt = ctx->type.dataType; + dt.MakeHandle(true); + dt.MakeReference(false); + + offset = AllocateVariableNotIn(dt, true, false, ctx); + + // Copy the handle + if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() ) + ctx->bc.Instr(asBC_RDSPtr); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + if( ctx->type.dataType.IsFuncdef() ) + ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo()); + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + + dt.MakeHandle(false); + dt.MakeReference(true); + + // Release previous temporary variable stored in the context (if any) + if( ctx->type.isTemporary ) + ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc); + + ctx->type.SetVariable(dt, offset, true); + } + + // Make sure the reference to the value is on the stack + // For objects, the reference needs to be dereferenced so the pointer on the stack is to the actual object + // For handles, the reference shouldn't be changed because the pointer on the stack should be to the handle + if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && ctx->type.dataType.IsReference() && !param.IsObjectHandle() ) + Dereference(ctx, true); + else if( ctx->type.isVariable && !(ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) ) + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + else if( ctx->type.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PshRPtr); + else if( ctx->type.dataType.IsObjectHandle() && !ctx->type.dataType.IsReference() ) + ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, false); + } + } + else + { + ProcessPropertyGetAccessor(ctx, node); + + if( dt.IsPrimitive() ) + { + IsVariableInitialized(&ctx->type, node); + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + + // Implicitly convert primitives to the parameter type + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV); + + if( ctx->type.isVariable ) + { + PushVariableOnStack(ctx, dt.IsReference()); + } + else if( ctx->type.isConstant ) + { + ConvertToVariable(ctx); + PushVariableOnStack(ctx, dt.IsReference()); + } + } + else + { + IsVariableInitialized(&ctx->type, node); + + // Implicitly convert primitives to the parameter type + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV); + + // Was the conversion successful? + if( !ctx->type.dataType.IsEqualExceptRef(dt) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), dt.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.Set(dt); + return -1; + } + + if( dt.IsObjectHandle() ) + ctx->type.isExplicitHandle = true; + + if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsNullHandle() && !dt.IsReference() ) + { + // Objects passed by value must be placed in temporary variables + // so that they are guaranteed to not be referenced anywhere else. + // The object must also be allocated on the heap, as the memory will + // be deleted by the called function. + + // Handles passed by value must also be placed in a temporary variable + // to guarantee that the object referred to isn't freed too early. + + // TODO: value on stack: How can we avoid this unnecessary allocation? + + // Don't make temporary copies of handles if it is going to be used + // for handle assignment anyway, i.e. REFCPY. + if( !(!isFunction && isMakingCopy && ctx->type.dataType.IsObjectHandle() && ctx->type.isVariable) ) + PrepareTemporaryVariable(node, ctx, true); + } + } + } + + // Don't put any pointer on the stack yet + if( param.IsReference() || ((param.IsObject() || param.IsFuncdef()) && !param.IsNullHandle()) ) + { + // &inout parameter may leave the reference on the stack already + if( refType != asTM_INOUTREF ) + { + asASSERT( ctx->type.isVariable || ctx->type.isTemporary || isMakingCopy ); + + if( ctx->type.isVariable || ctx->type.isTemporary ) + { + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset); + + ProcessDeferredParams(ctx); + } + } + } + + return 0; +} + +void asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray &args) +{ + // When a match has been found, compile the final byte code using correct parameter types + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + + asASSERT( descr->parameterTypes.GetLength() == args.GetLength() ); + + // If the function being called is the opAssign or copy constructor for the same type + // as the argument, then we should avoid making temporary copy of the argument + bool makingCopy = false; + if( descr->parameterTypes.GetLength() == 1 && + descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetTypeInfo()) || + (descr->objectType == 0 && args[0]->type.dataType.GetTypeInfo() && descr->name == args[0]->type.dataType.GetTypeInfo()->name)) ) + makingCopy = true; + + // Add code for arguments + asCExprContext e(engine); + for( int n = (int)args.GetLength()-1; n >= 0; n-- ) + { + // Make sure PrepareArgument doesn't use any variable that is already + // being used by any of the following argument expressions + int l = int(reservedVariables.GetLength()); + for( int m = n-1; m >= 0; m-- ) + args[m]->bc.GetVarsUsed(reservedVariables); + + PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], makingCopy); + reservedVariables.SetLength(l); + } + + bc->AddCode(&e.bc); +} + +void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray &args, bool addOneToOffset) +{ + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + + int offset = 0; + if( addOneToOffset ) + offset += AS_PTR_SIZE; + + // The address of where the return value should be stored is push on top of the arguments + if( descr->DoesReturnOnStack() ) + offset += AS_PTR_SIZE; + +#ifdef AS_DEBUG + // If the function being called is the opAssign or copy constructor for the same type + // as the argument, then we should avoid making temporary copy of the argument + bool makingCopy = false; + if( descr->parameterTypes.GetLength() == 1 && + descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetTypeInfo()) || + (descr->objectType == 0 && args[0]->type.dataType.GetTypeInfo() && descr->name == args[0]->type.dataType.GetTypeInfo()->name)) ) + makingCopy = true; +#endif + + // Move the objects that are sent by value to the stack just before the call + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsReference() ) + { + if( (descr->parameterTypes[n].IsObject() || descr->parameterTypes[n].IsFuncdef()) && !descr->parameterTypes[n].IsObjectHandle() ) + { + if( descr->inOutFlags[n] != asTM_INOUTREF ) + { +#ifdef AS_DEBUG + // This assert is inside AS_DEBUG because of the variable makingCopy which is only defined in debug mode + asASSERT( args[n]->type.isVariable || args[n]->type.isTemporary || makingCopy ); +#endif + + if( (args[n]->type.isVariable || args[n]->type.isTemporary) ) + { + if( !IsVariableOnHeap(args[n]->type.stackOffset) ) + // TODO: runtime optimize: Actually the reference can be pushed on the stack directly + // as the value allocated on the stack is guaranteed to be safe + bc->InstrWORD(asBC_GETREF, (asWORD)offset); + else + bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset); + } + } + if( args[n]->type.dataType.IsObjectHandle() ) + bc->InstrWORD(asBC_ChkNullS, (asWORD)offset); + } + else if( descr->inOutFlags[n] != asTM_INOUTREF ) + { + if( descr->parameterTypes[n].GetTokenType() == ttQuestion && + (args[n]->type.dataType.IsObject() || args[n]->type.dataType.IsFuncdef()) && + !args[n]->type.dataType.IsObjectHandle() ) + { + // Send the object as a reference to the object, + // and not to the variable holding the object + if( !IsVariableOnHeap(args[n]->type.stackOffset) ) + // TODO: runtime optimize: Actually the reference can be pushed on the stack directly + // as the value allocated on the stack is guaranteed to be safe + bc->InstrWORD(asBC_GETREF, (asWORD)offset); + else + bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset); + } + else + { + // If the variable is really an argument of @& type, then it is necessary + // to use asBC_GETOBJREF so the pointer is correctly dereferenced. + sVariable *var = variables->GetVariableByOffset(args[n]->type.stackOffset); + if (var == 0 || !var->type.IsReference()) + bc->InstrWORD(asBC_GETREF, (asWORD)offset); + else + bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset); + } + } + } + else if( descr->parameterTypes[n].IsObject() || descr->parameterTypes[n].IsFuncdef() ) + { + // TODO: value on stack: What can we do to avoid this unnecessary allocation? + // The object must be allocated on the heap, because this memory will be deleted in as_callfunc_xxx + asASSERT(IsVariableOnHeap(args[n]->type.stackOffset)); + + // The pointer in the variable will be moved to the stack + bc->InstrWORD(asBC_GETOBJ, (asWORD)offset); + + // Deallocate the variable slot so it can be reused, but do not attempt to + // free the content of the variable since it was moved to the stack for the call + DeallocateVariable(args[n]->type.stackOffset); + args[n]->type.isTemporary = false; + } + + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + } +} + +int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray &args, asCArray &namedArgs) +{ + asASSERT(node->nodeType == snArgList); + + // Count arguments + asCScriptNode *arg = node->firstChild; + int argCount = 0; + while( arg ) + { + if( arg->nodeType != snNamedArgument ) + argCount++; + arg = arg->next; + } + + // Prepare the arrays + args.SetLength(argCount); + int n; + for( n = 0; n < argCount; n++ ) + args[n] = 0; + + n = argCount-1; + + // Compile the arguments in reverse order (as they will be pushed on the stack) + bool anyErrors = false, inPositionalArguments = false; + arg = node->lastChild; + while( arg ) + { + asCScriptNode *asgNode = arg, *namedNode = 0; + if( asgNode->nodeType == snNamedArgument ) + { + if( inPositionalArguments ) + { + Error(TXT_POS_ARG_AFTER_NAMED_ARG, node); + return -1; + } + + asgNode = arg->firstChild->next; + namedNode = arg->firstChild; + + asASSERT( namedNode->nodeType == snIdentifier ); + } + else + inPositionalArguments = true; + + asCExprContext expr(engine); + int r = CompileAssignment(asgNode, &expr); + if( r < 0 ) anyErrors = true; + + asCExprContext *ctx = asNEW(asCExprContext)(engine); + if( ctx == 0 ) + { + // Out of memory + return -1; + } + MergeExprBytecodeAndType(ctx, &expr); + + if( inPositionalArguments ) + { + args[n] = ctx; + n--; + } + else + { + asSNamedArgument namedArg; + namedArg.name = asCString(&script->code[namedNode->tokenPos], namedNode->tokenLength); + namedArg.ctx = ctx; + + // Error out when multiple arguments with the same name are passed + for( asUINT a = 0; a < namedArgs.GetLength(); ++a ) + { + if( namedArgs[a].name == namedArg.name ) + { + Error(TXT_DUPLICATE_NAMED_ARG, asgNode); + anyErrors = true; + break; + } + } + + namedArgs.PushLast(namedArg); + } + + arg = arg->prev; + } + + return anyErrors ? -1 : 0; +} + +int asCCompiler::CompileDefaultAndNamedArgs(asCScriptNode *node, asCArray &args, int funcId, asCObjectType *objectType, asCArray *namedArgs) +{ + asCScriptFunction *func = builder->GetFunctionDescription(funcId); + if( func == 0 || args.GetLength() >= (asUINT)func->GetParamCount() ) + return 0; + + // Make sure to use the real function for virtual functions + if( func->funcType == asFUNC_VIRTUAL ) + { + asASSERT( objectType ); + func = objectType->virtualFunctionTable[func->vfTableIdx]; + } + + // Make sure none of the variables used in the previous arguments are reused in the default arguments + bool anyErrors = false; + int prevReservedVars = reservedVariables.GetLength(); + + int explicitArgs = (int)args.GetLength(); + + for( int p = 0; p < explicitArgs; p++ ) + args[p]->bc.GetVarsUsed(reservedVariables); + + // Make space for all the new arguments + args.SetLength(func->parameterTypes.GetLength()); + for( asUINT c = explicitArgs; c < args.GetLength(); c++ ) + args[c] = 0; + + // Add the named arguments to the argument list in the right position + if( namedArgs ) + { + for( asUINT n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &named = (*namedArgs)[n]; + named.ctx->bc.GetVarsUsed(reservedVariables); + + // Find the right spot to put it in + asUINT index = asUINT(-1); + for( asUINT j = 0; j < func->parameterTypes.GetLength(); ++j ) + { + if( func->parameterNames[j] == (*namedArgs)[n].name ) + { + index = j; + break; + } + } + + asASSERT( index < args.GetLength() ); + args[index] = named.ctx; + named.ctx = 0; + } + } + + // Compile the arguments in reverse order (as they will be pushed on the stack) + for( int n = (int)func->parameterTypes.GetLength() - 1; n >= explicitArgs; n-- ) + { + if( args[n] != 0 ) continue; + if( func->defaultArgs[n] == 0 ) { anyErrors = true; continue; } + + // Parse the default arg string + asCParser parser(builder); + asCScriptCode code; + code.SetCode("default arg", func->defaultArgs[n]->AddressOf(), false); + int r = parser.ParseExpression(&code); + if( r < 0 ) + { + asCString msg; + msg.Format(TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s, n, func->GetDeclaration()); + Error(msg, node); + anyErrors = true; + continue; + } + + asCScriptNode *arg = parser.GetScriptNode(); + + // Temporarily set the script code to the default arg expression + asCScriptCode *origScript = script; + script = &code; + + // Don't allow the expression to access local variables + isCompilingDefaultArg = true; + + // Temporarily set the namespace in the output function to the namespace of the called + // function so that the default arguments are evaluated in the correct namespace + asSNameSpace *origNameSpace = outFunc->nameSpace; + outFunc->nameSpace = func->nameSpace; + + asCExprContext expr(engine); + r = CompileExpression(arg, &expr); + + // Restore the namespace + outFunc->nameSpace = origNameSpace; + + // Don't allow address of class method + if( expr.methodName != "" ) + { + // TODO: Improve error message + Error(TXT_DEF_ARG_TYPE_DOESNT_MATCH, arg); + r = -1; + } + + // Make sure the expression can be implicitly converted to the parameter type + if( r >= 0 ) + { + asCArray funcs; + funcs.PushLast(func->id); + asCArray matches; + if( MatchArgument(funcs, matches, &expr, n) == 0 ) + { + Error(TXT_DEF_ARG_TYPE_DOESNT_MATCH, arg); + r = -1; + } + } + + isCompilingDefaultArg = false; + + script = origScript; + + if( r < 0 ) + { + asCString msg; + msg.Format(TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s, n, func->GetDeclaration()); + Error(msg, node); + anyErrors = true; + continue; + } + + args[n] = asNEW(asCExprContext)(engine); + if( args[n] == 0 ) + { + // Out of memory + reservedVariables.SetLength(prevReservedVars); + return -1; + } + + MergeExprBytecodeAndType(args[n], &expr); + } + + reservedVariables.SetLength(prevReservedVars); + return anyErrors ? -1 : 0; +} + +asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCArray *namedArgs, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope) +{ + asCArray origFuncs = funcs; // Keep the original list for error message + asUINT cost = 0; + asUINT n; + + if( funcs.GetLength() > 0 ) + { + // Check the number of parameters in the found functions + asUINT totalArgs = (asUINT)args.GetLength(); + if( namedArgs != 0 ) + totalArgs += (asUINT)namedArgs->GetLength(); + + for( n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]); + + if( desc->parameterTypes.GetLength() != totalArgs ) + { + bool noMatch = true; + if( totalArgs < desc->parameterTypes.GetLength() ) + { + // For virtual functions, the default args are defined in the real function of the object + if( desc->funcType == asFUNC_VIRTUAL ) + desc = objectType->virtualFunctionTable[desc->vfTableIdx]; + + // Count the number of default args + asUINT defaultArgs = 0; + for( asUINT d = 0; d < desc->defaultArgs.GetLength(); d++ ) + if( desc->defaultArgs[d] ) + defaultArgs++; + + if( totalArgs >= desc->parameterTypes.GetLength() - defaultArgs ) + noMatch = false; + } + + if( noMatch ) + { + // remove it from the list + if( n == funcs.GetLength()-1 ) + funcs.PopLast(); + else + funcs[n] = funcs.PopLast(); + n--; + } + } + } + + // Match functions with the parameters, and discard those that do not match + asCArray matchingFuncs; + matchingFuncs.SetLengthNoConstruct( funcs.GetLength() ); + for ( n = 0; n < funcs.GetLength(); ++n ) + { + matchingFuncs[n].funcId = funcs[n]; + matchingFuncs[n].cost = 0; + } + + // Match positionally passed arguments + for( n = 0; n < args.GetLength(); ++n ) + { + asCArray tempFuncs; + MatchArgument(funcs, tempFuncs, args[n], n, allowObjectConstruct); + + // Intersect the found functions with the list of matching functions + for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ ) + { + asUINT c; + for( c = 0; c < tempFuncs.GetLength(); c++ ) + { + if( matchingFuncs[f].funcId == tempFuncs[c].funcId ) + { + // Sum argument cost + matchingFuncs[f].cost += tempFuncs[c].cost; + break; + + } // End if match + } + + // Was the function a match? + if( c == tempFuncs.GetLength() ) + { + // No, remove it from the list + if( f == matchingFuncs.GetLength()-1 ) + matchingFuncs.PopLast(); + else + matchingFuncs[f] = matchingFuncs.PopLast(); + f--; + } + } + } + + // Match named arguments + if( namedArgs != 0 ) + { + for( asUINT i = 0; i < matchingFuncs.GetLength(); ++i ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(matchingFuncs[i].funcId); + if( desc->funcType == asFUNC_VIRTUAL ) + desc = objectType->virtualFunctionTable[desc->vfTableIdx]; + + // Match every named argument to an argument in the function + for( n = 0; n < namedArgs->GetLength(); ++n ) + (*namedArgs)[n].match = asUINT(-1); + + bool matchedAll = true; + for( asUINT j = 0; j < desc->parameterTypes.GetLength(); ++j ) + { + asUINT match = asUINT(-1); + for( n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &namedArg = (*namedArgs)[n]; + if( desc->parameterNames[j] == namedArg.name ) + { + namedArg.match = j; + match = n; + break; + } + } + + // Check that every position is filled somehow + if( j >= args.GetLength() ) + { + if( match == asUINT(-1) && !desc->defaultArgs[j] ) + { + // No argument was found for this, and there is no + // default, so it doesn't work. + matchedAll = false; + break; + } + } + else + { + if( match != asUINT(-1) ) + { + // Can't name an argument that was already passed + matchedAll = false; + break; + } + } + } + + // Check that every named argument was matched + if( matchedAll ) + { + for( n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &named = (*namedArgs)[n]; + + if( named.match == asUINT(-1) ) + { + matchedAll = false; + break; + } + + // Add to the cost + cost = MatchArgument(desc, named.ctx, named.match, allowObjectConstruct); + if( cost == asUINT(-1) ) + { + matchedAll = false; + break; + } + + matchingFuncs[i].cost += cost; + } + } + + if( !matchedAll ) + { + // Remove the function, we didn't match all the arguments. + if( i == matchingFuncs.GetLength()-1 ) + matchingFuncs.PopLast(); + else + matchingFuncs[i] = matchingFuncs.PopLast(); + i--; + } + } + } + + // Select the overload(s) with the lowest overall cost + funcs.SetLength(0); + asUINT bestCost = asUINT(-1); + for( n = 0; n < matchingFuncs.GetLength(); ++n ) + { + cost = matchingFuncs[n].cost; + if( cost < bestCost ) + { + funcs.SetLength(0); + bestCost = cost; + } + if( cost == bestCost ) + funcs.PushLast( matchingFuncs[n].funcId ); + } + + // Cost returned is equivalent to the best cost discovered + cost = bestCost; + } + + if( !isConstMethod ) + FilterConst(funcs); + + if( funcs.GetLength() != 1 && !silent ) + { + // Build a readable string of the function with parameter types + bool attemptsPassingClassMethod = false; + asCString str; + if( scope != "" && scope != "::" ) + str = scope + "::"; + str += name; + str += "("; + for( n = 0; n < args.GetLength(); n++ ) + { + if( n > 0 ) + str += ", "; + if( args[n]->methodName != "" ) + { + if( args[n]->IsClassMethod() ) + { + attemptsPassingClassMethod = true; + str += args[n]->type.dataType.GetTypeInfo()->GetName(); + str += "::"; + } + str += args[n]->methodName; + } + else + str += args[n]->type.dataType.Format(outFunc->nameSpace); + } + if( namedArgs != 0 ) + { + for( n = 0; n < namedArgs->GetLength(); n++ ) + { + if( n > 0 || args.GetLength() ) + str += ", "; + + asSNamedArgument &named = (*namedArgs)[n]; + str += named.name; + str += "="; + if( named.ctx->methodName != "" ) + str += named.ctx->methodName; + else + str += named.ctx->type.dataType.Format(outFunc->nameSpace); + } + } + str += ")"; + + if( isConstMethod ) + str += " const"; + + if( objectType && scope == "" ) + str = objectType->name + "::" + str; + + if( funcs.GetLength() == 0 ) + { + str.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, str.AddressOf()); + Error(str, node); + + if( attemptsPassingClassMethod ) + { + // Class methods must use delegate objects + Error(TXT_CANNOT_PASS_CLASS_METHOD_AS_ARG, node); + } + else + { + // Print the list of candidates + if( origFuncs.GetLength() > 0 ) + { + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false); + PrintMatchingFuncs(origFuncs, node, objectType); + } + } + } + else + { + asASSERT( attemptsPassingClassMethod == false ); + + str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf()); + Error(str, node); + + PrintMatchingFuncs(funcs, node, objectType); + } + } + + return cost; +} + +bool asCCompiler::CompileAutoType(asCDataType &type, asCExprContext &compiledCtx, asCScriptNode *node, asCScriptNode *errNode) +{ + if( node && node->nodeType == snAssignment ) + { + int r = CompileAssignment(node, &compiledCtx); + if( r >= 0 ) + { + // Must not have unused ambiguous names + if (compiledCtx.IsClassMethod() || compiledCtx.IsGlobalFunc()) + { + // TODO: Should mention that the problem is the ambiguous name + Error(TXT_CANNOT_RESOLVE_AUTO, errNode); + return false; + } + + // Must not have unused anonymous functions + if (compiledCtx.IsLambda()) + { + // TODO: Should mention that the problem is the anonymous function + Error(TXT_CANNOT_RESOLVE_AUTO, errNode); + return false; + } + + // Must not be a null handle + if (compiledCtx.type.dataType.IsNullHandle()) + { + // TODO: Should mention that the problem is the null pointer + Error(TXT_CANNOT_RESOLVE_AUTO, errNode); + return false; + } + + asCDataType newType = compiledCtx.type.dataType; + + // Handle const qualifier on auto + if (type.IsReadOnly()) + newType.MakeReadOnly(true); + else if (newType.IsPrimitive()) + newType.MakeReadOnly(false); + + // Handle reference/value stuff + newType.MakeReference(false); + if (!newType.IsObjectHandle()) + { + // We got a value object or an object reference. + // Turn the variable into a handle if specified + // as auto@, otherwise make it a 'value'. + if (type.IsHandleToAuto()) + { + if (newType.MakeHandle(true) < 0) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, errNode); + return false; + } + } + } + + type = newType; + return true; + } + + return false; + } + else + { + Error(TXT_CANNOT_RESOLVE_AUTO, errNode); + type = asCDataType::CreatePrimitive(ttInt, false); + return false; + } +} + +void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) +{ + // Get the data type + asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script, outFunc->nameSpace, false, outFunc->objectType); + + // Declare all variables in this declaration + asCScriptNode *node = decl->firstChild->next; + while( node ) + { + // If this is an auto type, we have to compile the assignment now to figure out the type + asCExprContext compiledCtx(engine); + bool preCompiled = false; + if (type.IsAuto()) + { + preCompiled = CompileAutoType(type, compiledCtx, node->next, node); + if (!preCompiled) + { + // If it wasn't possible to determine the type from the expression then there + // is no need to continue with the initialization. The error was already reported + // in CompileAutoType. + return; + } + } + + // Is the type allowed? + if( !type.CanBeInstantiated() ) + { + asCString str; + if( type.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf()); + else if( type.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + // Don't continue, as it will most likely lead to further + // errors that may just mislead the script writer + return; + } + + // A shared object may not declare variables of non-shared types + if( outFunc->IsShared() ) + { + asCTypeInfo *ot = type.GetTypeInfo(); + if( ot && !ot->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, ot->name.AddressOf()); + Error(msg, decl); + } + } + + // Get the name of the identifier + asCString name(&script->code[node->tokenPos], node->tokenLength); + + // Verify that the name isn't used by a dynamic data type + // TODO: Must check against registered funcdefs too + if( engine->GetRegisteredType(name.AddressOf(), outFunc->nameSpace) != 0 ) + { + asCString str; + str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf()); + Error(str, node); + } + + int offset = AllocateVariable(type, false); + if( variables->DeclareVariable(name.AddressOf(), type, offset, IsVariableOnHeap(offset)) < 0 ) + { + // TODO: It might be an out-of-memory too + + asCString str; + str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf()); + Error(str, node); + + // Don't continue after this error, as it will just + // lead to more errors that are likely false + return; + } + else + { + // Warn if this variable hides another variable in a higher scope + if( variables->parent && variables->parent->GetVariable(name.AddressOf()) ) + { + asCString str; + str.Format(TXT_s_HIDES_VAR_IN_OUTER_SCOPE, name.AddressOf()); + Warning(str, node); + } + } + + // Add marker that the variable has been declared + bc->VarDecl((int)outFunc->scriptData->variables.GetLength()); + outFunc->AddVariable(name, type, offset); + + // Keep the node for the variable decl + asCScriptNode *varNode = node; + + node = node->next; + + if( node == 0 || node->nodeType == snIdentifier ) + { + // Initialize with default constructor + CompileInitialization(0, bc, type, varNode, offset, 0, 0); + } + else + { + // Compile the initialization expression + asQWORD constantValue = 0; + if( CompileInitialization(node, bc, type, varNode, offset, &constantValue, 0, preCompiled ? &compiledCtx : 0) ) + { + // Check if the variable should be marked as pure constant + if( type.IsPrimitive() && type.IsReadOnly() ) + { + sVariable *v = variables->GetVariable(name.AddressOf()); + v->isPureConstant = true; + v->constantValue = constantValue; + } + } + node = node->next; + } + } + + bc->OptimizeLocally(tempVariableOffsets); +} + +// Returns true if the initialization expression is a constant expression +bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem, asCExprContext *preCompiled) +{ + bool isConstantExpression = false; + if( node && node->nodeType == snArgList ) + { + // Make sure it is an object and not a handle + if( type.GetTypeInfo() == 0 || type.IsObjectHandle() ) + { + Error(TXT_MUST_BE_OBJECT, node); + } + else + { + // Compile the arguments + asCArray args; + asCArray namedArgs; + if( CompileArgumentList(node, args, namedArgs) >= 0 ) + { + // Find all constructors + asCArray funcs; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) + { + if( type.GetTypeInfo()->flags & asOBJ_REF ) + funcs = beh->factories; + else + funcs = beh->constructors; + } + + asCString str = type.Format(outFunc->nameSpace); + MatchFunctions(funcs, args, node, str.AddressOf(), &namedArgs); + + if( funcs.GetLength() == 1 ) + { + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(type.GetTypeInfo()), &namedArgs); + + if( r == asSUCCESS ) + { + asCExprContext ctx(engine); + if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_REF) ) + { + if( isVarGlobOrMem == 0 ) + MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset); + else + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node); + ctx.bc.Instr(asBC_RDSPtr); + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + if( type.IsFuncdef()) + ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo()); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); + } + else + { + bool onHeap = false; + + if( isVarGlobOrMem == 0 ) + { + // When the object is allocated on the heap, the address where the + // reference will be stored must be pushed on the stack before the + // arguments. This reference on the stack is safe, even if the script + // is suspended during the evaluation of the arguments. + onHeap = IsVariableOnHeap(offset); + if( onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + else if( isVarGlobOrMem == 1 ) + { + // Push the address of the location where the variable will be stored on the stack. + // This reference is safe, because the addresses of the global variables cannot change. + onHeap = true; + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Value types may be allocated inline if they are POD types + onHeap = !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF); + if( onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + } + + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + // When the object is allocated on the stack, the address to the + // object is pushed on the stack after the arguments as the object pointer + if( !onHeap ) + { + if( isVarGlobOrMem == 2 ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + else + { + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + } + + PerformFunctionCall(funcs[0], &ctx, onHeap, &args, CastToObjectType(type.GetTypeInfo())); + + if( isVarGlobOrMem == 0 ) + { + // Mark the object in the local variable as initialized + ctx.bc.ObjInfo(offset, asOBJ_INIT); + } + } + bc->AddCode(&ctx.bc); + } + } + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx, asCExprContext); + } + } + } + else if( node && node->nodeType == snInitList ) + { + asCExprValue ti; + ti.Set(type); + ti.isVariable = (isVarGlobOrMem == 0); + ti.isTemporary = false; + ti.stackOffset = (short)offset; + ti.isLValue = true; + + CompileInitList(&ti, node, bc, isVarGlobOrMem); + } + else if( node && node->nodeType == snAssignment ) + { + // Compile the expression + asCExprContext newExpr(engine); + asCExprContext* expr; + int r = 0; + + if( preCompiled ) + { + expr = preCompiled; + } + else + { + expr = &newExpr; + r = CompileAssignment(node, expr); + } + + // Look for appropriate constructor + asCArray funcs; + asCArray args; + + // Handles must use the handle assignment operation. + // Types that are ASHANDLE must not allow the use of the constructor in this case, + // because it is ambiguous whether a value assignment or handle assignment will be done. + // Only do this if the expression is of the same type, as the expression is an assignment + // and an initialization constructor may not have the same meaning. + // TODO: Should allow initialization constructor if it is declared as allowed for implicit conversions. + if( !type.IsObjectHandle() && !expr->type.isExplicitHandle && + !(type.GetTypeInfo() && (type.GetTypeInfo()->GetFlags() & asOBJ_ASHANDLE)) && + type.IsEqualExceptRefAndConst(expr->type.dataType) ) + { + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) + { + if( type.GetTypeInfo()->flags & asOBJ_REF ) + funcs = beh->factories; + else + funcs = beh->constructors; + } + + asCString str = type.Format(outFunc->nameSpace); + args.PushLast(expr); + MatchFunctions(funcs, args, node, str.AddressOf(), 0, 0, 0, true); + + // Make sure the argument is of the right type (and not just compatible with the expression) + if (funcs.GetLength() == 1) + { + asCScriptFunction *f = engine->scriptFunctions[funcs[0]]; + if (!f->parameterTypes[0].IsEqualExceptRefAndConst(expr->type.dataType)) + funcs.PopLast(); + } + } + + if( funcs.GetLength() == 1 ) + { + // Use the constructor + + // TODO: clean-up: A large part of this is identical to the initalization with argList above + + // Add the default values for arguments not explicitly supplied + r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(type.GetTypeInfo())); + + if( r == asSUCCESS ) + { + asCExprContext ctx(engine); + if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_REF) ) + { + if( isVarGlobOrMem == 0 ) + MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset); + else + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node); + ctx.bc.Instr(asBC_RDSPtr); + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + if( type.IsFuncdef() ) + ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx.bc.InstrPTR(asBC_REFCPY, type.GetTypeInfo()); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); + } + else + { + bool onHeap = false; + + if( isVarGlobOrMem == 0 ) + { + // When the object is allocated on the heap, the address where the + // reference will be stored must be pushed on the stack before the + // arguments. This reference on the stack is safe, even if the script + // is suspended during the evaluation of the arguments. + onHeap = IsVariableOnHeap(offset); + if( onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + else if( isVarGlobOrMem == 1 ) + { + // Push the address of the location where the variable will be stored on the stack. + // This reference is safe, because the addresses of the global variables cannot change. + onHeap = true; + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Value types may be allocated inline if they are POD types + onHeap = !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF); + if( onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + } + + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + // When the object is allocated on the stack, the address to the + // object is pushed on the stack after the arguments as the object pointer + if( !onHeap ) + { + if( isVarGlobOrMem == 2 ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + else + { + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + } + + PerformFunctionCall(funcs[0], &ctx, onHeap, &args, CastToObjectType(type.GetTypeInfo())); + + if( isVarGlobOrMem == 0 ) + { + // Mark the object in the local variable as initialized + ctx.bc.ObjInfo(offset, asOBJ_INIT); + } + } + bc->AddCode(&ctx.bc); + } + } + else + { + // Call the default constructur, then call the assignment operator + asCExprContext ctx(engine); + + // Call the default constructor here + if( isVarGlobOrMem == 0 ) + CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); + else if( isVarGlobOrMem == 1 ) + CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem); + else if( isVarGlobOrMem == 2 ) + CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); + + if( r >= 0 ) + { + if( type.IsPrimitive() ) + { + if( type.IsReadOnly() && expr->type.isConstant ) + { + ImplicitConversion(expr, type, node, asIC_IMPLICIT_CONV); + + // Tell caller that the expression is a constant so it can mark the variable as pure constant + isConstantExpression = true; + *constantValue = expr->type.GetConstantData(); + } + + asCExprContext lctx(engine); + if( isVarGlobOrMem == 0 ) + lctx.type.SetVariable(type, offset, false); + else if( isVarGlobOrMem == 1 ) + { + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); + + // If it is an enum value, i.e. offset is negative, that is being compiled then + // we skip this as the bytecode won't be used anyway, only the constant value + if( offset >= 0 ) + lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + asASSERT( isVarGlobOrMem == 2 ); + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); + + // Load the reference of the primitive member into the register + lctx.bc.InstrSHORT(asBC_PSF, 0); + lctx.bc.Instr(asBC_RDSPtr); + lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + lctx.bc.Instr(asBC_PopRPtr); + } + lctx.type.dataType.MakeReadOnly(false); + lctx.type.isLValue = true; + + DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); + ProcessDeferredParams(&ctx); + } + else + { + // TODO: runtime optimize: Here we should look for the best matching constructor, instead of + // just the copy constructor. Only if no appropriate constructor is + // available should the assignment operator be used. + + asCExprContext lexpr(engine); + lexpr.type.Set(type); + if( isVarGlobOrMem == 0 ) + lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset)); + else if( isVarGlobOrMem == 1 ) + lexpr.type.dataType.MakeReference(true); + else if( isVarGlobOrMem == 2 ) + { + if( !lexpr.type.dataType.IsObject() || lexpr.type.dataType.IsFuncdef() || (lexpr.type.dataType.GetTypeInfo()->flags & asOBJ_REF) ) + lexpr.type.dataType.MakeReference(true); + } + + // Allow initialization of constant variables + lexpr.type.dataType.MakeReadOnly(false); + + if( type.IsObjectHandle() ) + lexpr.type.isExplicitHandle = true; + + if( isVarGlobOrMem == 0 ) + { + lexpr.bc.InstrSHORT(asBC_PSF, (short)offset); + lexpr.type.stackOffset = (short)offset; + lexpr.type.isVariable = true; + } + else if( isVarGlobOrMem == 1 ) + { + lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + lexpr.bc.InstrSHORT(asBC_PSF, 0); + lexpr.bc.Instr(asBC_RDSPtr); + lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + lexpr.type.stackOffset = -1; + } + lexpr.type.isLValue = true; + + + // If left expression resolves into a registered type + // check if the assignment operator is overloaded, and check + // the type of the right hand expression. If none is found + // the default action is a direct copy if it is the same type + // and a simple assignment. + bool assigned = false; + // Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called + if( (lexpr.type.dataType.IsObject() || lexpr.type.dataType.IsFuncdef()) && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetTypeInfo() && (lexpr.type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE))) ) + { + bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); + assigned = CompileOverloadedDualOperator(node, &lexpr, expr, false, &ctx, useHndlAssign); + if( assigned ) + { + // Pop the resulting value + if( !ctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); + + // Release the argument + ProcessDeferredParams(&ctx); + + // Release temporary variable that may be allocated by the overloaded operator + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + } + } + + if( !assigned ) + { + PrepareForAssignment(&lexpr.type.dataType, expr, node, false); + + // If the expression is constant and the variable also is constant + // then mark the variable as pure constant. This will allow the compiler + // to optimize expressions with this variable. + if( type.IsReadOnly() && expr->type.isConstant ) + { + isConstantExpression = true; + *constantValue = expr->type.GetConstantQW(); + } + + // Add expression code to bytecode + MergeExprBytecode(&ctx, expr); + + // Add byte code for storing value of expression in variable + ctx.bc.AddCode(&lexpr.bc); + + PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr->type, &ctx.bc); + + ctx.bc.Instr(asBC_PopPtr); + + ProcessDeferredParams(&ctx); + } + } + } + + bc->AddCode(&ctx.bc); + } + } + else + { + asASSERT( node == 0 ); + + // Call the default constructor here, as no explicit initialization is done + if( isVarGlobOrMem == 0 ) + CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), bc, errNode); + else if( isVarGlobOrMem == 1 ) + CallDefaultConstructor(type, offset, true, bc, errNode, isVarGlobOrMem); + else if( isVarGlobOrMem == 2 ) + { + if( !(type.IsObject() || type.IsFuncdef()) || type.IsReference() || (type.GetTypeInfo()->flags & asOBJ_REF) ) + CallDefaultConstructor(type, offset, true, bc, errNode, isVarGlobOrMem); + else + CallDefaultConstructor(type, offset, false, bc, errNode, isVarGlobOrMem); + } + } + + return isConstantExpression; +} + +void asCCompiler::CompileInitList(asCExprValue *var, asCScriptNode *node, asCByteCode *bc, int isVarGlobOrMem) +{ + // Check if the type supports initialization lists + if( var->dataType.GetTypeInfo() == 0 || + var->dataType.GetBehaviour()->listFactory == 0 ) + { + asCString str; + str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + return; + } + + // Construct the buffer with the elements + + // Find the list factory + int funcId = var->dataType.GetBehaviour()->listFactory; + asASSERT( engine->scriptFunctions[funcId]->listPattern ); + + // TODO: runtime optimize: A future optimization should be to use the stack space directly + // for small buffers so that the dynamic allocation is skipped + + // Create a new special object type for the lists. Both asCRestore and the + // context exception handler will need this to know how to parse the buffer. + asCObjectType *listPatternType = engine->GetListPatternType(funcId); + + // Allocate a temporary variable to hold the pointer to the buffer + int bufferVar = AllocateVariable(asCDataType::CreateType(listPatternType, false), true); + asUINT bufferSize = 0; + + // Evaluate all elements of the list + asCExprContext valueExpr(engine); + asCScriptNode *el = node; + asSListPatternNode *patternNode = engine->scriptFunctions[listPatternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern; + int elementsInSubList = -1; + int r = CompileInitListElement(patternNode, el, engine->GetTypeIdFromDataType(asCDataType::CreateType(listPatternType, false)), short(bufferVar), bufferSize, valueExpr.bc, elementsInSubList); + asASSERT( r || patternNode == 0 ); + UNUSED_VAR(r); + + // After all values have been evaluated we know the final size of the buffer + asCExprContext allocExpr(engine); + allocExpr.bc.InstrSHORT_DW(asBC_AllocMem, short(bufferVar), bufferSize); + + // Merge the bytecode into the final sequence + bc->AddCode(&allocExpr.bc); + bc->AddCode(&valueExpr.bc); + + // The object itself is the last to be created and will receive the pointer to the buffer + asCArray args; + asCExprContext arg1(engine); + arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false)); + arg1.type.dataType.MakeReference(true); + arg1.bc.InstrSHORT(asBC_PshVPtr, short(bufferVar)); + args.PushLast(&arg1); + + asCExprContext ctx(engine); + + if( var->isVariable ) + { + asASSERT( isVarGlobOrMem == 0 ); + + if( var->dataType.GetTypeInfo()->GetFlags() & asOBJ_REF ) + { + ctx.bc.AddCode(&arg1.bc); + + // Call factory and store the handle in the given variable + PerformFunctionCall(funcId, &ctx, false, &args, 0, true, var->stackOffset); + ctx.bc.Instr(asBC_PopPtr); + } + else + { + // Call the constructor + + // When the object is allocated on the heap, the address where the + // reference will be stored must be pushed on the stack before the + // arguments. This reference on the stack is safe, even if the script + // is suspended during the evaluation of the arguments. + bool onHeap = IsVariableOnHeap(var->stackOffset); + if( onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset); + + ctx.bc.AddCode(&arg1.bc); + + // When the object is allocated on the stack, the address to the + // object is pushed on the stack after the arguments as the object pointer + if( !onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset); + + PerformFunctionCall(funcId, &ctx, onHeap, &args, CastToObjectType(var->dataType.GetTypeInfo())); + + // Mark the object in the local variable as initialized + ctx.bc.ObjInfo(var->stackOffset, asOBJ_INIT); + } + } + else + { + if( var->dataType.GetTypeInfo()->GetFlags() & asOBJ_REF ) + { + ctx.bc.AddCode(&arg1.bc); + + PerformFunctionCall(funcId, &ctx, false, &args); + + ctx.bc.Instr(asBC_RDSPtr); + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + if (var->dataType.IsFuncdef()) + ctx.bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetTypeInfo()); + ctx.bc.Instr(asBC_PopPtr); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + else + { + bool onHeap = true; + + // Put the address where the object pointer will be placed on the stack + if( isVarGlobOrMem == 1 ) + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue()); + else + { + onHeap = !(var->dataType.IsObject() || var->dataType.IsFuncdef()) || var->dataType.IsReference() || (var->dataType.GetTypeInfo()->flags & asOBJ_REF); + if( onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + } + + // Add the address of the list buffer as the argument + ctx.bc.AddCode(&arg1.bc); + + if( !onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)var->stackOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(outFunc->objectType, false))); + } + + // Call the ALLOC instruction to allocate memory and invoke constructor + PerformFunctionCall(funcId, &ctx, onHeap, &args, CastToObjectType(var->dataType.GetTypeInfo())); + } + } + + bc->AddCode(&ctx.bc); + + // Free the temporary buffer. The FREE instruction will make sure to destroy + // each element in the buffer so there is no need to do this manually + bc->InstrW_PTR(asBC_FREE, short(bufferVar), listPatternType); + ReleaseTemporaryVariable(bufferVar, bc); +} + +int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &bcInit, int &elementsInSubList) +{ + if( patternNode->type == asLPT_START ) + { + if( valueNode == 0 || valueNode->nodeType != snInitList ) + { + Error(TXT_EXPECTED_LIST, valueNode); + return -1; + } + + // Compile all values until asLPT_END + patternNode = patternNode->next; + asCScriptNode *node = valueNode->firstChild; + while( patternNode->type != asLPT_END ) + { + // Check for missing value here, else the error reporting will not have a source position to report the error for + if( node == 0 && patternNode->type == asLPT_TYPE ) + { + Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, valueNode); + return -1; + } + + asCScriptNode *errNode = node; + int r = CompileInitListElement(patternNode, node, bufferTypeId, bufferVar, bufferSize, bcInit, elementsInSubList); + if( r < 0 ) return r; + + if( r == 1 ) + { + asASSERT( engine->ep.disallowEmptyListElements ); + // Empty elements in the middle are not allowed + Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode); + } + + asASSERT( patternNode ); + } + + if( node ) + { + Error(TXT_TOO_MANY_VALUES_FOR_LIST, valueNode); + return -1; + } + + // Move to the next node + valueNode = valueNode->next; + patternNode = patternNode->next; + } + else if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) + { + // TODO: list: repeat_inner should make sure the list has the same size as the inner list, i.e. square area + // TODO: list: repeat_prev should make sure the list is the same size as the previous + + asEListPatternNodeType repeatType = patternNode->type; + asCScriptNode *firstValue = valueNode; + + // The following values will be repeated N times + patternNode = patternNode->next; + + // Keep track of the patternNode so it can be reset + asSListPatternNode *nextNode = patternNode; + + // Align the buffer size to 4 bytes in case previous value was smaller than 4 bytes + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + // The first dword will hold the number of elements in the list + asDWORD currSize = bufferSize; + bufferSize += 4; + asUINT countElements = 0; + + int elementsInSubSubList = -1; + + asCExprContext ctx(engine); + while( valueNode ) + { + patternNode = nextNode; + asCScriptNode *errNode = valueNode; + int r = CompileInitListElement(patternNode, valueNode, bufferTypeId, bufferVar, bufferSize, ctx.bc, elementsInSubSubList); + if( r < 0 ) return r; + + if( r == 0 ) + countElements++; + else + { + asASSERT( r == 1 && engine->ep.disallowEmptyListElements ); + if( valueNode ) + { + // Empty elements in the middle are not allowed + Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode); + } + } + } + + if( countElements == 0 ) + { + // Skip the sub pattern that was expected to be repeated, otherwise the caller will try to match these when we return + patternNode = nextNode; + if( patternNode->type == asLPT_TYPE ) + patternNode = patternNode->next; + else if( patternNode->type == asLPT_START ) + { + int subCount = 1; + do + { + patternNode = patternNode->next; + if( patternNode->type == asLPT_START ) + subCount++; + else if( patternNode->type == asLPT_END ) + subCount--; + } while( subCount > 0 ); + patternNode = patternNode->next; + } + } + + // For repeat_same each repeated sublist must have the same size to form a rectangular array + if( repeatType == asLPT_REPEAT_SAME && elementsInSubList != -1 && asUINT(elementsInSubList) != countElements ) + { + if( countElements < asUINT(elementsInSubList) ) + Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, firstValue); + else + Error(TXT_TOO_MANY_VALUES_FOR_LIST, firstValue); + + return -1; + } + else + { + // Return to caller the amount of elments in this sublist + elementsInSubList = countElements; + } + + // The first dword in the buffer will hold the number of elements + bcInit.InstrSHORT_DW_DW(asBC_SetListSize, bufferVar, currSize, countElements); + + // Add the values + bcInit.AddCode(&ctx.bc); + } + else if( patternNode->type == asLPT_TYPE ) + { + bool isEmpty = false; + + // Determine the size of the element + asUINT size = 0; + + asCDataType dt = reinterpret_cast(patternNode)->dataType; + + if( valueNode->nodeType == snAssignment || valueNode->nodeType == snInitList ) + { + asCExprContext lctx(engine); + asCExprContext rctx(engine); + + if( valueNode->nodeType == snAssignment ) + { + // Compile the assignment expression + CompileAssignment(valueNode, &rctx); + + if( dt.GetTokenType() == ttQuestion ) + { + // Make sure the type is not ambiguous + DetermineSingleFunc(&rctx, valueNode); + + // We now know the type + dt = rctx.type.dataType; + dt.MakeReadOnly(false); + dt.MakeReference(false); + + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + // Place the type id in the buffer + bcInit.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, engine->GetTypeIdFromDataType(dt)); + bufferSize += 4; + } + } + else if( valueNode->nodeType == snInitList ) + { + if( dt.GetTokenType() == ttQuestion ) + { + // Can't use init lists with var type as it is not possible to determine what type should be allocated + asCString str; + str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, "?"); + Error(str.AddressOf(), valueNode); + rctx.type.SetDummy(); + dt = rctx.type.dataType; + } + else + { + // Allocate a temporary variable that will be initialized with the list + int offset = AllocateVariable(dt, true); + + rctx.type.Set(dt); + rctx.type.isVariable = true; + rctx.type.isTemporary = true; + rctx.type.stackOffset = (short)offset; + + CompileInitList(&rctx.type, valueNode, &rctx.bc, 0); + + // Put the object on the stack + rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset); + + // It is a reference that we place on the stack + rctx.type.dataType.MakeReference(true); + } + } + + // Determine size of the element + if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetTypeInfo()->flags & asOBJ_VALUE)) ) + size = dt.GetSizeInMemoryBytes(); + else + size = AS_PTR_SIZE*4; + + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( size >= 4 && (bufferSize & 0x3) ) + bufferSize += 4 - (bufferSize & 0x3); + + // Compile the lvalue + lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + lctx.type.Set(dt); + lctx.type.isLValue = true; + if( dt.IsPrimitive() ) + { + lctx.bc.Instr(asBC_PopRPtr); + lctx.type.dataType.MakeReference(true); + } + else if( dt.IsObjectHandle() || + dt.GetTypeInfo()->flags & asOBJ_REF ) + { + lctx.type.isExplicitHandle = true; + lctx.type.dataType.MakeReference(true); + } + else + { + asASSERT( dt.GetTypeInfo()->flags & asOBJ_VALUE ); + + // Make sure the object has been constructed before the assignment + // TODO: runtime optimize: Use copy constructor instead of assignment to initialize the objects + asSTypeBehaviour *beh = dt.GetBehaviour(); + int func = 0; + if( beh ) func = beh->construct; + if( func == 0 && (dt.GetTypeInfo()->flags & asOBJ_POD) == 0 ) + { + asCString str; + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName()); + Error(str, valueNode); + } + else if( func ) + { + // Call the constructor as a normal function + bcInit.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + + asCExprContext ctx(engine); + PerformFunctionCall(func, &ctx, false, 0, CastToObjectType(dt.GetTypeInfo())); + bcInit.AddCode(&ctx.bc); + } + } + + if( lctx.type.dataType.IsNullHandle() ) + { + // Don't add any code to assign a null handle. RefCpy doesn't work without a known type. + // The buffer is already initialized to zero in asBC_AllocMem anyway. + asASSERT( rctx.bc.GetLastInstr() == asBC_PshNull ); + asASSERT( reinterpret_cast(patternNode)->dataType.GetTokenType() == ttQuestion ); + } + else + { + asCExprContext ctx(engine); + DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); + + if( !lctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + + ProcessDeferredParams(&ctx); + + bcInit.AddCode(&ctx.bc); + } + } + else + { + if( builder->engine->ep.disallowEmptyListElements ) + { + // Empty elements are not allowed, except if it is the last in the list + isEmpty = true; + } + else + { + // There is no specific value so we need to fill it with a default value + if( dt.GetTokenType() == ttQuestion ) + { + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + // Place the type id for a null handle in the buffer + bcInit.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, 0); + bufferSize += 4; + + dt = asCDataType::CreateNullHandle(); + + // No need to initialize the handle as the buffer is already initialized with zeroes + } + else if( dt.GetTypeInfo() && dt.GetTypeInfo()->flags & asOBJ_VALUE ) + { + // For value types with default constructor we need to call the constructor + asSTypeBehaviour *beh = dt.GetBehaviour(); + int func = 0; + if( beh ) func = beh->construct; + if( func == 0 && (dt.GetTypeInfo()->flags & asOBJ_POD) == 0 ) + { + asCString str; + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName()); + Error(str, valueNode); + } + else if( func ) + { + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + // Call the constructor as a normal function + bcInit.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + + asCExprContext ctx(engine); + PerformFunctionCall(func, &ctx, false, 0, CastToObjectType(dt.GetTypeInfo())); + bcInit.AddCode(&ctx.bc); + } + } + else if( !dt.IsObjectHandle() && dt.GetTypeInfo() && dt.GetTypeInfo()->flags & asOBJ_REF ) + { + // For ref types (not handles) we need to call the default factory + asSTypeBehaviour *beh = dt.GetBehaviour(); + int func = 0; + if( beh ) func = beh->factory; + if( func == 0 ) + { + asCString str; + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetTypeInfo()->GetName()); + Error(str, valueNode); + } + else if( func ) + { + asCExprContext rctx(engine); + PerformFunctionCall(func, &rctx, false, 0, CastToObjectType(dt.GetTypeInfo())); + + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + asCExprContext lctx(engine); + lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + lctx.type.Set(dt); + lctx.type.isLValue = true; + lctx.type.isExplicitHandle = true; + lctx.type.dataType.MakeReference(true); + + asCExprContext ctx(engine); + DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); + + if( !lctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + + ProcessDeferredParams(&ctx); + + bcInit.AddCode(&ctx.bc); + } + } + } + } + + if( !isEmpty ) + { + // Determine size of the element + if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetTypeInfo()->flags & asOBJ_VALUE)) ) + size = dt.GetSizeInMemoryBytes(); + else + size = AS_PTR_SIZE*4; + asASSERT( size <= 4 || (size & 0x3) == 0 ); + + bufferSize += size; + } + + // Move to the next element + patternNode = patternNode->next; + valueNode = valueNode->next; + + if( isEmpty ) + { + // The caller will determine if the empty element should be ignored or not + return 1; + } + } + else + asASSERT( false ); + + return 0; +} + +void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc) +{ + // Don't clear the hasReturn flag if this is an empty statement + // to avoid false errors of 'not all paths return' + if( statement->nodeType != snExpressionStatement || statement->firstChild ) + *hasReturn = false; + + if( statement->nodeType == snStatementBlock ) + CompileStatementBlock(statement, true, hasReturn, bc); + else if( statement->nodeType == snIf ) + CompileIfStatement(statement, hasReturn, bc); + else if( statement->nodeType == snFor ) + CompileForStatement(statement, bc); + else if( statement->nodeType == snWhile ) + CompileWhileStatement(statement, bc); + else if( statement->nodeType == snDoWhile ) + CompileDoWhileStatement(statement, bc); + else if( statement->nodeType == snExpressionStatement ) + CompileExpressionStatement(statement, bc); + else if( statement->nodeType == snBreak ) + CompileBreakStatement(statement, bc); + else if( statement->nodeType == snContinue ) + CompileContinueStatement(statement, bc); + else if( statement->nodeType == snSwitch ) + CompileSwitchStatement(statement, hasReturn, bc); + else if( statement->nodeType == snReturn ) + { + CompileReturnStatement(statement, bc); + *hasReturn = true; + } +} + +void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCode *bc) +{ + // TODO: inheritance: Must guarantee that all options in the switch case call a constructor, or that none call it. + + // Reserve label for break statements + int breakLabel = nextLabel++; + breakLabels.PushLast(breakLabel); + + // Add a variable scope that will be used by CompileBreak + // to know where to stop deallocating variables + AddVariableScope(true, false); + + //--------------------------- + // Compile the switch expression + //------------------------------- + + // Compile the switch expression + asCExprContext expr(engine); + CompileAssignment(snode->firstChild, &expr); + + // Verify that the expression is a primitive type + if( !expr.type.dataType.IsIntegerType() && !expr.type.dataType.IsUnsignedType() ) + { + Error(TXT_SWITCH_MUST_BE_INTEGRAL, snode->firstChild); + return; + } + + ProcessPropertyGetAccessor(&expr, snode); + + // TODO: Need to support 64bit integers + // Convert the expression to a 32bit variable + asCDataType to; + if( expr.type.dataType.IsIntegerType() ) + to.SetTokenType(ttInt); + else if( expr.type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + + // Make sure the value is in a variable + if( expr.type.dataType.IsReference() ) + ConvertToVariable(&expr); + + ImplicitConversion(&expr, to, snode->firstChild, asIC_IMPLICIT_CONV, true); + + ConvertToVariable(&expr); + int offset = expr.type.stackOffset; + + ProcessDeferredParams(&expr); + + //------------------------------- + // Determine case values and labels + //-------------------------------- + + // Remember the first label so that we can later pass the + // correct label to each CompileCase() + int firstCaseLabel = nextLabel; + int defaultLabel = 0; + + asCArray caseValues; + asCArray caseLabels; + + // Compile all case comparisons and make them jump to the right label + asCScriptNode *cnode = snode->firstChild->next; + while( cnode ) + { + // Each case should have a constant expression + if( cnode->firstChild && cnode->firstChild->nodeType == snExpression ) + { + // Compile expression + asCExprContext c(engine); + CompileExpression(cnode->firstChild, &c); + + // Verify that the result is a constant + if( !c.type.isConstant ) + Error(TXT_SWITCH_CASE_MUST_BE_CONSTANT, cnode->firstChild); + + // Verify that the result is an integral number + if (!c.type.dataType.IsIntegerType() && !c.type.dataType.IsUnsignedType()) + Error(TXT_SWITCH_MUST_BE_INTEGRAL, cnode->firstChild); + else + { + ImplicitConversion(&c, to, cnode->firstChild, asIC_IMPLICIT_CONV, true); + + // Has this case been declared already? + if (caseValues.IndexOf(c.type.GetConstantDW()) >= 0) + Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild); + + // TODO: Optimize: We can insert the numbers sorted already + + // Store constant for later use + caseValues.PushLast(c.type.GetConstantDW()); + + // Reserve label for this case + caseLabels.PushLast(nextLabel++); + } + } + else + { + // TODO: It shouldn't be necessary for the default case to be the last one. + // Is default the last case? + if( cnode->next ) + { + Error(TXT_DEFAULT_MUST_BE_LAST, cnode); + break; + } + + // Reserve label for this case + defaultLabel = nextLabel++; + } + + cnode = cnode->next; + } + + // check for empty switch + if (caseValues.GetLength() == 0) + { + Error(TXT_EMPTY_SWITCH, snode); + return; + } + + if( defaultLabel == 0 ) + defaultLabel = breakLabel; + + //--------------------------------- + // Output the optimized case comparisons + // with jumps to the case code + //------------------------------------ + + // Sort the case values by increasing value. Do the sort together with the labels + // A simple bubble sort is sufficient since we don't expect a huge number of values + for( asUINT fwd = 1; fwd < caseValues.GetLength(); fwd++ ) + { + for( int bck = fwd - 1; bck >= 0; bck-- ) + { + int bckp = bck + 1; + if( caseValues[bck] > caseValues[bckp] ) + { + // Swap the values in both arrays + int swap = caseValues[bckp]; + caseValues[bckp] = caseValues[bck]; + caseValues[bck] = swap; + + swap = caseLabels[bckp]; + caseLabels[bckp] = caseLabels[bck]; + caseLabels[bck] = swap; + } + else + break; + } + } + + // Find ranges of consecutive numbers + asCArray ranges; + ranges.PushLast(0); + asUINT n; + for( n = 1; n < caseValues.GetLength(); ++n ) + { + // We can join numbers that are less than 5 numbers + // apart since the output code will still be smaller + if( caseValues[n] > caseValues[n-1] + 5 ) + ranges.PushLast(n); + } + + // If the value is larger than the largest case value, jump to default + int tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[caseValues.GetLength()-1]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JP, defaultLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + + // TODO: runtime optimize: We could possibly optimize this even more by doing a + // binary search instead of a linear search through the ranges + + // For each range + int range; + for( range = 0; range < (int)ranges.GetLength(); range++ ) + { + // Find the largest value in this range + int maxRange = caseValues[ranges[range]]; + int index = ranges[range]; + for( ; (index < (int)caseValues.GetLength()) && (caseValues[index] <= maxRange + 5); index++ ) + maxRange = caseValues[index]; + + // If there are only 2 numbers then it is better to compare them directly + if( index - ranges[range] > 2 ) + { + // If the value is smaller than the smallest case value in the range, jump to default + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JS, defaultLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + + int nextRangeLabel = nextLabel++; + // If this is the last range we don't have to make this test + if( range < (int)ranges.GetLength() - 1 ) + { + // If the value is larger than the largest case value in the range, jump to the next range + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, maxRange); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JP, nextRangeLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + } + + // Jump forward according to the value + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]); + expr.bc.InstrW_W_W(asBC_SUBi, tmpOffset, offset, tmpOffset); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + expr.bc.JmpP(tmpOffset, maxRange - caseValues[ranges[range]]); + + // Add the list of jumps to the correct labels (any holes, jump to default) + index = ranges[range]; + for( int i = caseValues[index]; i <= maxRange; i++ ) + { + if( caseValues[index] == i ) + expr.bc.InstrINT(asBC_JMP, caseLabels[index++]); + else + expr.bc.InstrINT(asBC_JMP, defaultLabel); + } + + expr.bc.Label((short)nextRangeLabel); + } + else + { + // Simply make a comparison with each value + for( int i = ranges[range]; i < index; ++i ) + { + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[i]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JZ, caseLabels[i]); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + } + } + } + + // Catch any value that falls trough + expr.bc.InstrINT(asBC_JMP, defaultLabel); + + // Release the temporary variable previously stored + ReleaseTemporaryVariable(expr.type, &expr.bc); + + // TODO: optimize: Should optimize each piece individually + expr.bc.OptimizeLocally(tempVariableOffsets); + + //---------------------------------- + // Output case implementations + //---------------------------------- + + // Compile case implementations, each one with the label before it + cnode = snode->firstChild->next; + while( cnode ) + { + // Each case should have a constant expression + if( cnode->firstChild && cnode->firstChild->nodeType == snExpression ) + { + expr.bc.Label((short)firstCaseLabel++); + + CompileCase(cnode->firstChild->next, &expr.bc); + } + else + { + expr.bc.Label((short)defaultLabel); + + // Is default the last case? + if( cnode->next ) + { + // We've already reported this error + break; + } + + CompileCase(cnode->firstChild, &expr.bc); + } + + cnode = cnode->next; + } + + //-------------------------------- + + bc->AddCode(&expr.bc); + + // Add break label + bc->Label((short)breakLabel); + + breakLabels.PopLast(); + RemoveVariableScope(); +} + +void asCCompiler::CompileCase(asCScriptNode *node, asCByteCode *bc) +{ + bool isFinished = false; + bool hasReturn = false; + bool hasUnreachableCode = false; + while( node ) + { + if( !hasUnreachableCode && (hasReturn || isFinished) ) + { + hasUnreachableCode = true; + Warning(TXT_UNREACHABLE_CODE, node); + break; + } + + if( node->nodeType == snBreak || node->nodeType == snContinue ) + isFinished = true; + + asCByteCode statement(engine); + if( node->nodeType == snDeclaration ) + { + Error(TXT_DECL_IN_SWITCH, node); + + // Compile it anyway to avoid further compiler errors + CompileDeclaration(node, &statement); + } + else + CompileStatement(node, &hasReturn, &statement); + + LineInstr(bc, node->tokenPos); + bc->AddCode(&statement); + + if( !hasCompileErrors ) + asASSERT( tempVariables.GetLength() == 0 ); + + node = node->next; + } +} + +void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCByteCode *bc) +{ + // We will use one label for the if statement + // and possibly another for the else statement + int afterLabel = nextLabel++; + + // Compile the expression + asCExprContext expr(engine); + int r = CompileAssignment(inode->firstChild, &expr); + if( r == 0 ) + { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), inode, asIC_IMPLICIT_CONV); + + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + if( !expr.type.isConstant ) + { + ProcessPropertyGetAccessor(&expr, inode); + + ConvertToVariable(&expr); + + // Add a test + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } +#if AS_SIZEOF_BOOL == 1 + else if( expr.type.GetConstantB() == 0 ) +#else + else if (expr.type.GetConstantDW() == 0) +#endif + { + // Jump to the else case + bc->InstrINT(asBC_JMP, afterLabel); + + // TODO: Should we warn that the expression will always go to the else? + } + } + } + + // Compile the if statement + bool origIsConstructorCalled = m_isConstructorCalled; + + bool hasReturn1; + asCByteCode ifBC(engine); + CompileStatement(inode->firstChild->next, &hasReturn1, &ifBC); + + // Add the byte code + LineInstr(bc, inode->firstChild->next->tokenPos); + bc->AddCode(&ifBC); + + if( inode->firstChild->next->nodeType == snExpressionStatement && inode->firstChild->next->firstChild == 0 ) + { + // Don't allow if( expr ); + Error(TXT_IF_WITH_EMPTY_STATEMENT, inode->firstChild->next); + } + + // If one of the statements call the constructor, the other must as well + // otherwise it is possible the constructor is never called + bool constructorCall1 = false; + bool constructorCall2 = false; + if( !origIsConstructorCalled && m_isConstructorCalled ) + constructorCall1 = true; + + // Do we have an else statement? + if( inode->firstChild->next != inode->lastChild ) + { + // Reset the constructor called flag so the else statement can call the constructor too + m_isConstructorCalled = origIsConstructorCalled; + + int afterElse = 0; + if( !hasReturn1 ) + { + afterElse = nextLabel++; + + // Add jump to after the else statement + bc->InstrINT(asBC_JMP, afterElse); + } + + // Add label for the else statement + bc->Label((short)afterLabel); + + bool hasReturn2; + asCByteCode elseBC(engine); + CompileStatement(inode->lastChild, &hasReturn2, &elseBC); + + // Add byte code for the else statement + LineInstr(bc, inode->lastChild->tokenPos); + bc->AddCode(&elseBC); + + if( inode->lastChild->nodeType == snExpressionStatement && inode->lastChild->firstChild == 0 ) + { + // Don't allow if( expr ) {} else; + Error(TXT_ELSE_WITH_EMPTY_STATEMENT, inode->lastChild); + } + + if( !hasReturn1 ) + { + // Add label for the end of else statement + bc->Label((short)afterElse); + } + + // The if statement only has return if both alternatives have + *hasReturn = hasReturn1 && hasReturn2; + + if( !origIsConstructorCalled && m_isConstructorCalled ) + constructorCall2 = true; + } + else + { + // Add label for the end of if statement + bc->Label((short)afterLabel); + *hasReturn = false; + } + + // Make sure both or neither conditions call a constructor + if( (constructorCall1 && !constructorCall2) || + (constructorCall2 && !constructorCall1) ) + { + Error(TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR, inode); + } + + m_isConstructorCalled = origIsConstructorCalled || constructorCall1 || constructorCall2; +} + +void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use three labels for the for loop + int conditionLabel = nextLabel++; + int afterLabel = nextLabel++; + int continueLabel = nextLabel++; + int insideLabel = nextLabel++; + + continueLabels.PushLast(continueLabel); + breakLabels.PushLast(afterLabel); + + //--------------------------------------- + // Compile the initialization statement + asCByteCode initBC(engine); + LineInstr(&initBC, fnode->firstChild->tokenPos); + if( fnode->firstChild->nodeType == snDeclaration ) + CompileDeclaration(fnode->firstChild, &initBC); + else + CompileExpressionStatement(fnode->firstChild, &initBC); + + //----------------------------------- + // Compile the condition statement + asCExprContext expr(engine); + asCScriptNode *second = fnode->firstChild->next; + if( second->firstChild ) + { + int r = CompileAssignment(second->firstChild, &expr); + if( r >= 0 ) + { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), second->firstChild, asIC_IMPLICIT_CONV); + + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, second); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + ProcessPropertyGetAccessor(&expr, second); + + // If expression is false exit the loop + ConvertToVariable(&expr); + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JNZ, insideLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + expr.bc.OptimizeLocally(tempVariableOffsets); + + // Prepend the line instruction for the condition + asCByteCode tmp(engine); + LineInstr(&tmp, second->firstChild->tokenPos); + tmp.AddCode(&expr.bc); + expr.bc.AddCode(&tmp); + } + } + } + + //--------------------------- + // Compile the increment statement(s) + asCByteCode nextBC(engine); + asCScriptNode *cnode = second->next; + while( cnode && cnode->nodeType == snExpressionStatement && cnode != fnode->lastChild ) + { + LineInstr(&nextBC, cnode->tokenPos); + CompileExpressionStatement(cnode, &nextBC); + cnode = cnode->next; + } + + //------------------------------ + // Compile loop statement + bool hasReturn; + asCByteCode forBC(engine); + CompileStatement(fnode->lastChild, &hasReturn, &forBC); + + //------------------------------- + // Join the code pieces + bc->AddCode(&initBC); + bc->InstrDWORD(asBC_JMP, conditionLabel); + + bc->Label((short)insideLabel); + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrPTR(asBC_JitEntry, 0); + + LineInstr(bc, fnode->lastChild->tokenPos); + bc->AddCode(&forBC); + + bc->Label((short)continueLabel); + bc->AddCode(&nextBC); + + bc->Label((short)conditionLabel); + if( expr.bc.GetLastInstr() == -1 ) + // There is no condition, so we just always jump + bc->InstrDWORD(asBC_JMP, insideLabel); + else + bc->AddCode(&expr.bc); + + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + // Deallocate variables in this block, in reverse order + for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + CallDestructor(v->type, v->stackOffset, v->onHeap, bc); + + // Don't deallocate function parameters + if( v->stackOffset > 0 ) + DeallocateVariable(v->stackOffset); + } + + RemoveVariableScope(); +} + +void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use two labels for the while loop + int beforeLabel = nextLabel++; + int afterLabel = nextLabel++; + + continueLabels.PushLast(beforeLabel); + breakLabels.PushLast(afterLabel); + + // Add label before the expression + bc->Label((short)beforeLabel); + + // Compile expression + asCExprContext expr(engine); + int r = CompileAssignment(wnode->firstChild, &expr); + if( r == 0 ) + { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->firstChild, asIC_IMPLICIT_CONV); + + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + ProcessPropertyGetAccessor(&expr, wnode); + + // Add byte code for the expression + ConvertToVariable(&expr); + + // Jump to end of statement if expression is false + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } + } + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrPTR(asBC_JitEntry, 0); + + // Compile statement + bool hasReturn; + asCByteCode whileBC(engine); + CompileStatement(wnode->lastChild, &hasReturn, &whileBC); + + // Add byte code for the statement + LineInstr(bc, wnode->lastChild->tokenPos); + bc->AddCode(&whileBC); + + // Jump to the expression + bc->InstrINT(asBC_JMP, beforeLabel); + + // Add label after the statement + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + RemoveVariableScope(); +} + +void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use two labels for the while loop + int beforeLabel = nextLabel++; + int beforeTest = nextLabel++; + int afterLabel = nextLabel++; + + continueLabels.PushLast(beforeTest); + breakLabels.PushLast(afterLabel); + + // Add label before the statement + bc->Label((short)beforeLabel); + + // Compile statement + bool hasReturn; + asCByteCode whileBC(engine); + CompileStatement(wnode->firstChild, &hasReturn, &whileBC); + + // Add byte code for the statement + LineInstr(bc, wnode->firstChild->tokenPos); + bc->AddCode(&whileBC); + + // Add label before the expression + bc->Label((short)beforeTest); + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrPTR(asBC_JitEntry, 0); + + // Add a line instruction + LineInstr(bc, wnode->lastChild->tokenPos); + + // Compile expression + asCExprContext expr(engine); + CompileAssignment(wnode->lastChild, &expr); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetTypeInfo() && (expr.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->lastChild, asIC_IMPLICIT_CONV); + + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + ProcessPropertyGetAccessor(&expr, wnode); + + // Add byte code for the expression + ConvertToVariable(&expr); + + // Jump to next iteration if expression is true + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JNZ, beforeLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } + + // Add label after the statement + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + RemoveVariableScope(); +} + +void asCCompiler::CompileBreakStatement(asCScriptNode *node, asCByteCode *bc) +{ + if( breakLabels.GetLength() == 0 ) + { + Error(TXT_INVALID_BREAK, node); + return; + } + + // Add destructor calls for all variables that will go out of scope + // Put this clean up in a block to allow exception handler to understand them + bc->Block(true); + asCVariableScope *vs = variables; + while( !vs->isBreakScope ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc); + + vs = vs->parent; + } + bc->Block(false); + + bc->InstrINT(asBC_JMP, breakLabels[breakLabels.GetLength()-1]); +} + +void asCCompiler::CompileContinueStatement(asCScriptNode *node, asCByteCode *bc) +{ + if( continueLabels.GetLength() == 0 ) + { + Error(TXT_INVALID_CONTINUE, node); + return; + } + + // Add destructor calls for all variables that will go out of scope + // Put this clean up in a block to allow exception handler to understand them + bc->Block(true); + asCVariableScope *vs = variables; + while( !vs->isContinueScope ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc); + + vs = vs->parent; + } + bc->Block(false); + + bc->InstrINT(asBC_JMP, continueLabels[continueLabels.GetLength()-1]); +} + +void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *bc) +{ + if( enode->firstChild ) + { + // Compile the expression + asCExprContext expr(engine); + CompileAssignment(enode->firstChild, &expr); + + // Must not have unused ambiguous names + if( expr.IsClassMethod() || expr.IsGlobalFunc() ) + Error(TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME, enode); + + // Must not have unused anonymous functions + if( expr.IsLambda() ) + Error(TXT_INVALID_EXPRESSION_LAMBDA, enode); + + // If we get here and there is still an unprocessed property + // accessor, then process it as a get access. Don't call if there is + // already a compile error, or we might report an error that is not valid + if( !hasCompileErrors ) + ProcessPropertyGetAccessor(&expr, enode); + + // Pop the value from the stack + if( !expr.type.dataType.IsPrimitive() ) + expr.bc.Instr(asBC_PopPtr); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr.type, &expr.bc); + + ProcessDeferredParams(&expr); + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } +} + +void asCCompiler::PrepareTemporaryVariable(asCScriptNode *node, asCExprContext *ctx, bool forceOnHeap) +{ + // The input can be either an object or funcdef, either as handle or reference + asASSERT(ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()); + + // If the object already is stored in temporary variable then nothing needs to be done + // Note, a type can be temporary without being a variable, in which case it is holding off + // on releasing a previously used object. + if( ctx->type.isTemporary && ctx->type.isVariable && + !(forceOnHeap && !IsVariableOnHeap(ctx->type.stackOffset)) ) + { + // If the temporary object is currently not a reference + // the expression needs to be reevaluated to a reference + if( !ctx->type.dataType.IsReference() ) + { + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + ctx->type.dataType.MakeReference(true); + } + + return; + } + + // Allocate temporary variable + asCDataType dt = ctx->type.dataType; + dt.MakeReference(false); + dt.MakeReadOnly(false); + + int offset = AllocateVariable(dt, true, forceOnHeap); + + // Objects stored on the stack are not considered references + dt.MakeReference(IsVariableOnHeap(offset)); + + asCExprValue lvalue; + lvalue.Set(dt); + lvalue.isExplicitHandle = ctx->type.isExplicitHandle; + bool isExplicitHandle = ctx->type.isExplicitHandle; + + CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false); + + // Push the reference to the temporary variable on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + + ctx->type.Set(dt); + ctx->type.isTemporary = true; + ctx->type.stackOffset = (short)offset; + ctx->type.isVariable = true; + ctx->type.isExplicitHandle = isExplicitHandle; + ctx->type.dataType.MakeReference(IsVariableOnHeap(offset)); +} + +void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) +{ + // Get return type and location + sVariable *v = variables->GetVariable("return"); + + // Basic validations + if( v->type.GetSizeOnStackDWords() > 0 && !rnode->firstChild ) + { + Error(TXT_MUST_RETURN_VALUE, rnode); + return; + } + else if( v->type.GetSizeOnStackDWords() == 0 && rnode->firstChild ) + { + Error(TXT_CANT_RETURN_VALUE, rnode); + return; + } + + // Compile the expression + if( rnode->firstChild ) + { + // Compile the expression + asCExprContext expr(engine); + int r = CompileAssignment(rnode->firstChild, &expr); + if( r < 0 ) return; + + if( v->type.IsReference() ) + { + // The expression that gives the reference must not use any of the + // variables that must be destroyed upon exit, because then it means + // reference will stay alive while the clean-up is done, which could + // potentially mean that the reference is invalidated by the clean-up. + // + // When the function is returning a reference, the clean-up of the + // variables must be done before the evaluation of the expression. + // + // A reference to a global variable, or a class member for class methods + // should be allowed to be returned. + + if( !(expr.type.dataType.IsReference() || + (expr.type.dataType.IsObject() && !expr.type.dataType.IsObjectHandle())) ) + { + // Clean up the potential deferred parameters + ProcessDeferredParams(&expr); + Error(TXT_NOT_VALID_REFERENCE, rnode); + return; + } + + // No references to local variables, temporary variables, or parameters + // are allowed to be returned, since they go out of scope when the function + // returns. Even reference parameters are disallowed, since it is not possible + // to know the scope of them. The exception is the 'this' pointer, which + // is treated by the compiler as a local variable, but isn't really so. + if( (expr.type.isVariable && !(expr.type.stackOffset == 0 && outFunc->objectType)) || expr.type.isTemporary ) + { + // Clean up the potential deferred parameters + ProcessDeferredParams(&expr); + Error(TXT_CANNOT_RETURN_REF_TO_LOCAL, rnode); + return; + } + + // The type must match exactly as we cannot convert + // the reference without loosing the original value + if( !(v->type.IsEqualExceptConst(expr.type.dataType) || + ((expr.type.dataType.IsObject() || expr.type.dataType.IsFuncdef()) && + !expr.type.dataType.IsObjectHandle() && + v->type.IsEqualExceptRefAndConst(expr.type.dataType))) || + (!v->type.IsReadOnly() && expr.type.dataType.IsReadOnly()) ) + { + // Clean up the potential deferred parameters + ProcessDeferredParams(&expr); + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); + Error(str, rnode); + return; + } + + // The expression must not have any deferred expressions, because the evaluation + // of these cannot be done without keeping the reference which is not safe + if( expr.deferredParams.GetLength() ) + { + // Clean up the potential deferred parameters + ProcessDeferredParams(&expr); + Error(TXT_REF_CANT_BE_RETURNED_DEFERRED_PARAM, rnode); + return; + } + + // Make sure the expression isn't using any local variables that + // will need to be cleaned up before the function completes + asCArray usedVars; + expr.bc.GetVarsUsed(usedVars); + for( asUINT n = 0; n < usedVars.GetLength(); n++ ) + { + int var = GetVariableSlot(usedVars[n]); + if( var != -1 ) + { + asCDataType dt = variableAllocations[var]; + if( dt.IsObject() ) + { + ProcessDeferredParams(&expr); + Error(TXT_REF_CANT_BE_RETURNED_LOCAL_VARS, rnode); + return; + } + } + } + + // Can't return the reference if could point to a local variable + if( expr.type.isRefToLocal ) + { + ProcessDeferredParams(&expr); + Error(TXT_REF_CANT_BE_TO_LOCAL_VAR, rnode); + return; + } + + // All objects in the function must be cleaned up before the expression + // is evaluated, otherwise there is a possibility that the cleanup will + // invalidate the reference. + + // Destroy the local variables before loading + // the reference into the register. This will + // be done before the expression is evaluated. + DestroyVariables(bc); + + + // For primitives the reference is already in the register, + // but for non-primitives the reference is on the stack so we + // need to load it into the register + if( !expr.type.dataType.IsPrimitive() ) + { + if( !expr.type.dataType.IsObjectHandle() && + expr.type.dataType.IsReference() ) + expr.bc.Instr(asBC_RDSPtr); + + expr.bc.Instr(asBC_PopRPtr); + } + + // There are no temporaries to release so we're done + } + else // if( !v->type.IsReference() ) + { + ProcessPropertyGetAccessor(&expr, rnode); + + // Prepare the value for assignment + IsVariableInitialized(&expr.type, rnode->firstChild); + + if( v->type.IsPrimitive() ) + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + + // Implicitly convert the value to the return type + ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV); + + // Verify that the conversion was successful + if( expr.type.dataType != v->type ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); + Error(str, rnode); + return; + } + else + { + ConvertToVariable(&expr); + + // Clean up the local variables and process deferred parameters + DestroyVariables(&expr.bc); + ProcessDeferredParams(&expr); + + ReleaseTemporaryVariable(expr.type, &expr.bc); + + // Load the variable in the register + if( v->type.GetSizeOnStackDWords() == 1 ) + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + else + expr.bc.InstrSHORT(asBC_CpyVtoR8, expr.type.stackOffset); + } + } + else if( v->type.IsObject() || v->type.IsFuncdef() ) + { + // Value types are returned on the stack, in a location + // that has been reserved by the calling function. + if( outFunc->DoesReturnOnStack() ) + { + // TODO: runtime optimize: If the return type has a constructor that takes the type of the expression, + // it should be called directly instead of first converting the expression and + // then copy the value. + if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) ) + { + ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV); + if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); + Error(str, rnode->firstChild); + return; + } + } + + int offset = outFunc->objectType ? -AS_PTR_SIZE : 0; + CompileInitAsCopy(v->type, offset, &expr.bc, &expr, rnode->firstChild, true); + + // Clean up the local variables and process deferred parameters + DestroyVariables(&expr.bc); + ProcessDeferredParams(&expr); + } + else + { + asASSERT( (v->type.GetTypeInfo()->flags & asOBJ_REF) || v->type.IsFuncdef() ); + + // Prepare the expression to be loaded into the object + // register. This will place the reference in local variable + PrepareArgument(&v->type, &expr, rnode->firstChild, false, 0); + + // Pop the reference to the temporary variable + expr.bc.Instr(asBC_PopPtr); + + // Clean up the local variables and process deferred parameters + DestroyVariables(&expr.bc); + ProcessDeferredParams(&expr); + + // Load the object pointer into the object register + // LOADOBJ also clears the address in the variable + expr.bc.InstrSHORT(asBC_LOADOBJ, expr.type.stackOffset); + + // LOADOBJ cleared the address in the variable so the object will not be freed + // here, but the temporary variable must still be freed so the slot can be reused + // By releasing without the bytecode we do just that. + ReleaseTemporaryVariable(expr.type, 0); + } + } + } + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } + else + { + // For functions that don't return anything + // we just detroy the local variables + DestroyVariables(bc); + } + + // Jump to the end of the function + bc->InstrINT(asBC_JMP, 0); +} + +void asCCompiler::DestroyVariables(asCByteCode *bc) +{ + // Call destructor on all variables except for the function parameters + // Put the clean-up in a block to allow exception handler to understand this + bc->Block(true); + asCVariableScope *vs = variables; + while( vs ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + if( vs->variables[n]->stackOffset > 0 ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, vs->variables[n]->onHeap, bc); + + vs = vs->parent; + } + bc->Block(false); +} + +void asCCompiler::AddVariableScope(bool isBreakScope, bool isContinueScope) +{ + variables = asNEW(asCVariableScope)(variables); + if( variables == 0 ) + { + // Out of memory + return; + } + variables->isBreakScope = isBreakScope; + variables->isContinueScope = isContinueScope; +} + +void asCCompiler::RemoveVariableScope() +{ + if( variables ) + { + asCVariableScope *var = variables; + variables = variables->parent; + asDELETE(var,asCVariableScope); + } +} + +void asCCompiler::Error(const asCString &msg, asCScriptNode *node) +{ + asCString str; + + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + builder->WriteError(script->name, msg, r, c); + + hasCompileErrors = true; +} + +void asCCompiler::Warning(const asCString &msg, asCScriptNode *node) +{ + asCString str; + + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + builder->WriteWarning(script->name, msg, r, c); +} + +void asCCompiler::Information(const asCString &msg, asCScriptNode *node) +{ + asCString str; + + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + builder->WriteInfo(script->name, msg, r, c, false); +} + +void asCCompiler::PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node, asCObjectType *inType) +{ + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *func = builder->GetFunctionDescription(funcs[n]); + if( inType && func->funcType == asFUNC_VIRTUAL ) + func = inType->virtualFunctionTable[func->vfTableIdx]; + + builder->WriteInfo(script->name, func->GetDeclaration(true, false, true), r, c, false); + } +} + +int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary, bool forceOnHeap, asCExprContext *ctx) +{ + int l = int(reservedVariables.GetLength()); + ctx->bc.GetVarsUsed(reservedVariables); + int var = AllocateVariable(type, isTemporary, forceOnHeap); + reservedVariables.SetLength(l); + return var; +} + +int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary, bool forceOnHeap) +{ + asCDataType t(type); + t.MakeReference(false); + + if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 ) + t.SetTokenType(ttInt); + + if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 2 ) + t.SetTokenType(ttDouble); + + // Only null handles have the token type unrecognized token + asASSERT( t.IsObjectHandle() || t.GetTokenType() != ttUnrecognizedToken ); + + bool isOnHeap = true; + if( t.IsPrimitive() || + (t.GetTypeInfo() && (t.GetTypeInfo()->GetFlags() & asOBJ_VALUE) && !forceOnHeap) ) + { + // Primitives and value types (unless overridden) are allocated on the stack + isOnHeap = false; + } + + // Find a free location with the same type + for( asUINT n = 0; n < freeVariables.GetLength(); n++ ) + { + int slot = freeVariables[n]; + + if( variableAllocations[slot].IsEqualExceptConst(t) && + variableIsTemporary[slot] == isTemporary && + variableIsOnHeap[slot] == isOnHeap ) + { + // We can't return by slot, must count variable sizes + int offset = GetVariableOffset(slot); + + // Verify that it is not in the list of reserved variables + bool isUsed = false; + if( reservedVariables.GetLength() ) + isUsed = reservedVariables.Exists(offset); + + if( !isUsed ) + { + if( n != freeVariables.GetLength() - 1 ) + freeVariables[n] = freeVariables.PopLast(); + else + freeVariables.PopLast(); + + if( isTemporary ) + tempVariables.PushLast(offset); + + return offset; + } + } + } + + variableAllocations.PushLast(t); + variableIsTemporary.PushLast(isTemporary); + variableIsOnHeap.PushLast(isOnHeap); + + int offset = GetVariableOffset((int)variableAllocations.GetLength()-1); + + if( isTemporary ) + { + // Add offset to the currently allocated temporary variables + tempVariables.PushLast(offset); + + // Add offset to all known offsets to temporary variables, whether allocated or not + tempVariableOffsets.PushLast(offset); + } + + return offset; +} + +int asCCompiler::GetVariableOffset(int varIndex) +{ + // Return offset to the last dword on the stack + + // Start at 1 as offset 0 is reserved for the this pointer (or first argument for global functions) + int varOffset = 1; + + // Skip lower variables + for( int n = 0; n < varIndex; n++ ) + { + if( !variableIsOnHeap[n] && variableAllocations[n].IsObject() ) + varOffset += variableAllocations[n].GetSizeInMemoryDWords(); + else + varOffset += variableAllocations[n].GetSizeOnStackDWords(); + } + + if( varIndex < (int)variableAllocations.GetLength() ) + { + // For variables larger than 1 dword the returned offset should be to the last dword + int size; + if( !variableIsOnHeap[varIndex] && variableAllocations[varIndex].IsObject() ) + size = variableAllocations[varIndex].GetSizeInMemoryDWords(); + else + size = variableAllocations[varIndex].GetSizeOnStackDWords(); + if( size > 1 ) + varOffset += size-1; + } + + return varOffset; +} + + +int asCCompiler::GetVariableSlot(int offset) +{ + int varOffset = 1; + for( asUINT n = 0; n < variableAllocations.GetLength(); n++ ) + { + if( !variableIsOnHeap[n] && variableAllocations[n].IsObject() ) + varOffset += -1 + variableAllocations[n].GetSizeInMemoryDWords(); + else + varOffset += -1 + variableAllocations[n].GetSizeOnStackDWords(); + + if( varOffset == offset ) + return n; + + varOffset++; + } + + return -1; +} + +bool asCCompiler::IsVariableOnHeap(int offset) +{ + int varSlot = GetVariableSlot(offset); + if( varSlot < 0 ) + { + // This happens for function arguments that are considered as on the heap + return true; + } + + return variableIsOnHeap[varSlot]; +} + +void asCCompiler::DeallocateVariable(int offset) +{ + // Remove temporary variable + int n; + for( n = 0; n < (int)tempVariables.GetLength(); n++ ) + { + if( offset == tempVariables[n] ) + { + if( n == (int)tempVariables.GetLength()-1 ) + tempVariables.PopLast(); + else + tempVariables[n] = tempVariables.PopLast(); + break; + } + } + + // Mark the variable slot available for new allocations + n = GetVariableSlot(offset); + if( n != -1 ) + { + freeVariables.PushLast(n); + return; + } + + // We might get here if the variable was implicitly declared + // because it was used before a formal declaration, in this case + // the offset is 0x7FFF + + asASSERT(offset == 0x7FFF); +} + +void asCCompiler::ReleaseTemporaryVariable(asCExprValue &t, asCByteCode *bc) +{ + if( t.isTemporary ) + { + ReleaseTemporaryVariable(t.stackOffset, bc); + t.isTemporary = false; + } +} + +void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc) +{ + asASSERT( tempVariables.Exists(offset) ); + + if( bc ) + { + // We need to call the destructor on the true variable type + int n = GetVariableSlot(offset); + asASSERT( n >= 0 ); + if( n >= 0 ) + { + asCDataType dt = variableAllocations[n]; + bool isOnHeap = variableIsOnHeap[n]; + + // Call destructor + CallDestructor(dt, offset, isOnHeap, bc); + } + } + + DeallocateVariable(offset); +} + +void asCCompiler::Dereference(asCExprContext *ctx, bool generateCode) +{ + if( ctx->type.dataType.IsReference() ) + { + if( ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef() ) + { + ctx->type.dataType.MakeReference(false); + if( generateCode ) + ctx->bc.Instr(asBC_RDSPtr); + } + else + { + // This should never happen as primitives are treated differently + asASSERT(false); + } + } +} + +bool asCCompiler::IsVariableInitialized(asCExprValue *type, asCScriptNode *node) +{ + // No need to check if there is no variable scope + if( variables == 0 ) return true; + + // Temporary variables are assumed to be initialized + if( type->isTemporary ) return true; + + // Verify that it is a variable + if( !type->isVariable ) return true; + + // Find the variable + sVariable *v = variables->GetVariableByOffset(type->stackOffset); + + // The variable isn't found if it is a constant, in which case it is guaranteed to be initialized + if( v == 0 ) return true; + + if( v->isInitialized ) return true; + + // Complex types don't need this test + if( v->type.IsObject() || v->type.IsFuncdef() ) return true; + + // Mark as initialized so that the user will not be bothered again + v->isInitialized = true; + + // Write warning + asCString str; + str.Format(TXT_s_NOT_INITIALIZED, (const char *)v->name.AddressOf()); + Warning(str, node); + + return false; +} + +void asCCompiler::PrepareOperand(asCExprContext *ctx, asCScriptNode *node) +{ + // Check if the variable is initialized (if it indeed is a variable) + IsVariableInitialized(&ctx->type, node); + + asCDataType to = ctx->type.dataType; + to.MakeReference(false); + + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + ProcessDeferredParams(ctx); +} + +void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asCExprContext *rctx, asCScriptNode *node, bool toTemporary, asCExprContext *lvalueExpr) +{ + // Reserve the temporary variables used in the lvalue expression so they won't end up being used by the rvalue too + int l = int(reservedVariables.GetLength()); + if( lvalueExpr ) lvalueExpr->bc.GetVarsUsed(reservedVariables); + + + ProcessPropertyGetAccessor(rctx, node); + + // Make sure the rvalue is initialized if it is a variable + IsVariableInitialized(&rctx->type, node); + + if( lvalue->IsPrimitive() ) + { + if( rctx->type.dataType.IsPrimitive() ) + { + if( rctx->type.dataType.IsReference() ) + { + // Cannot do implicit conversion of references so we first convert the reference to a variable + ConvertToVariableNotIn(rctx, lvalueExpr); + } + } + + // Implicitly convert the value to the right type + ImplicitConversion(rctx, *lvalue, node, asIC_IMPLICIT_CONV); + + // Check data type + if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + rctx->type.SetDummy(); + } + + // Make sure the rvalue is a variable + if( !rctx->type.isVariable ) + ConvertToVariableNotIn(rctx, lvalueExpr); + } + else + { + asCDataType to = *lvalue; + to.MakeReference(false); + + // TODO: ImplicitConversion should know to do this by itself + // First convert to a handle which will do a reference cast + if( !lvalue->IsObjectHandle() && + (lvalue->GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) ) + to.MakeHandle(true); + + // Don't allow the implicit conversion to create an object + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, !toTemporary); + + if( !lvalue->IsObjectHandle() && + (lvalue->GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) ) + { + // Then convert to a reference, which will validate the handle + to.MakeHandle(false); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, !toTemporary); + } + + // Check data type + if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + else + { + // If the assignment will be made with the copy behaviour then the rvalue must not be a reference + asASSERT(!lvalue->IsObject() || !rctx->type.dataType.IsReference()); + } + } + + // Unreserve variables + reservedVariables.SetLength(l); +} + +bool asCCompiler::IsLValue(asCExprValue &type) +{ + if( !type.isLValue ) return false; + if( type.dataType.IsReadOnly() ) return false; + if( !type.dataType.IsObject() && !type.isVariable && !type.dataType.IsReference() ) return false; + return true; +} + +int asCCompiler::PerformAssignment(asCExprValue *lvalue, asCExprValue *rvalue, asCByteCode *bc, asCScriptNode *node) +{ + if( lvalue->dataType.IsReadOnly() ) + { + Error(TXT_REF_IS_READ_ONLY, node); + return -1; + } + + if( lvalue->dataType.IsPrimitive() ) + { + if( lvalue->isVariable ) + { + // Copy the value between the variables directly + if( lvalue->dataType.GetSizeInMemoryDWords() == 1 ) + bc->InstrW_W(asBC_CpyVtoV4, lvalue->stackOffset, rvalue->stackOffset); + else + bc->InstrW_W(asBC_CpyVtoV8, lvalue->stackOffset, rvalue->stackOffset); + + // Mark variable as initialized + sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset); + if( v ) v->isInitialized = true; + } + else if( lvalue->dataType.IsReference() ) + { + // Copy the value of the variable to the reference in the register + int s = lvalue->dataType.GetSizeInMemoryBytes(); + if( s == 1 ) + bc->InstrSHORT(asBC_WRTV1, rvalue->stackOffset); + else if( s == 2 ) + bc->InstrSHORT(asBC_WRTV2, rvalue->stackOffset); + else if( s == 4 ) + bc->InstrSHORT(asBC_WRTV4, rvalue->stackOffset); + else if( s == 8 ) + bc->InstrSHORT(asBC_WRTV8, rvalue->stackOffset); + } + else + { + Error(TXT_NOT_VALID_LVALUE, node); + return -1; + } + } + else if( !lvalue->isExplicitHandle ) + { + asCExprContext ctx(engine); + ctx.type = *lvalue; + Dereference(&ctx, true); + *lvalue = ctx.type; + bc->AddCode(&ctx.bc); + + asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour(); + if( beh && beh->copy && beh->copy != engine->scriptTypeBehaviours.beh.copy ) + { + asCExprContext res(engine); + PerformFunctionCall(beh->copy, &res, false, 0, CastToObjectType(lvalue->dataType.GetTypeInfo())); + + bc->AddCode(&res.bc); + *lvalue = res.type; + } + else if( beh && beh->copy == engine->scriptTypeBehaviours.beh.copy ) + { + // Call the default copy operator for script classes + // This is done differently because the default copy operator + // is registered as returning int&, but in reality it returns + // a reference to the object. + // TODO: Avoid this special case by implementing a copystub for + // script classes that uses the default copy operator + bc->Call(asBC_CALLSYS, beh->copy, 2*AS_PTR_SIZE); + bc->Instr(asBC_PshRPtr); + } + else + { + // Default copy operator + if( lvalue->dataType.GetSizeInMemoryDWords() == 0 || + !(lvalue->dataType.GetTypeInfo()->flags & asOBJ_POD) ) + { + asCString msg; + msg.Format(TXT_NO_DEFAULT_COPY_OP_FOR_s, lvalue->dataType.GetTypeInfo()->name.AddressOf()); + Error(msg, node); + return -1; + } + + // Copy larger data types from a reference + // TODO: runtime optimize: COPY should pop both arguments and store the reference in the register. + bc->InstrSHORT_DW(asBC_COPY, (short)lvalue->dataType.GetSizeInMemoryDWords(), engine->GetTypeIdFromDataType(lvalue->dataType)); + } + } + else + { + // TODO: The object handle can be stored in a variable as well + if( !lvalue->dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + if( lvalue->dataType.IsFuncdef() ) + bc->InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetTypeInfo()); + + // Mark variable as initialized + if( variables ) + { + sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset); + if( v ) v->isInitialized = true; + } + } + + return 0; +} + +bool asCCompiler::CompileRefCast(asCExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode) +{ + bool conversionDone = false; + + asCArray ops; + asUINT n; + + // A ref cast must not remove the constness + bool isConst = ctx->type.dataType.IsObjectConst(); + + // Find a suitable opCast or opImplCast method + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + for( n = 0; ot && n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( (isExplicit && func->name == "opCast") || + func->name == "opImplCast" ) + { + // Is the operator for the output type? + if( func->returnType.GetTypeInfo() != to.GetTypeInfo() ) + continue; + + // Can't call a non-const function on a const object + if( isConst && !func->IsReadOnly() ) + continue; + + ops.PushLast(func->id); + } + } + + // Filter the list by constness to remove const methods if there are matching non-const methods + FilterConst(ops, !isConst); + + // It shouldn't be possible to have more than one + // TODO: Should be allowed to have different behaviours for const and non-const references + asASSERT( ops.GetLength() <= 1 ); + + // Should only have one behaviour for each output type + if( ops.GetLength() == 1 ) + { + conversionDone = true; + if( generateCode ) + { + // TODO: runtime optimize: Instead of producing bytecode for checking if the handle is + // null, we can create a special CALLSYS instruction that checks + // if the object pointer is null and if so sets the object register + // to null directly without executing the function. + // + // Alternatively I could force the ref cast behaviours be global + // functions with 1 parameter, even though they should still be + // registered with RegisterObjectBehaviour() + + if( (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOHANDLE)) + { + // Add code to avoid calling the cast behaviour if the handle is already null, + // because that will raise a null pointer exception due to the cast behaviour + // being a class method, and the this pointer cannot be null. + + if (!ctx->type.isVariable) + { + Dereference(ctx, true); + ConvertToVariable(ctx); + } + + // The reference on the stack will not be used + ctx->bc.Instr(asBC_PopPtr); + + // TODO: runtime optimize: should have immediate comparison for null pointer + int offset = AllocateVariable(asCDataType::CreateNullHandle(), true); + // TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though) + ctx->bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset); + ctx->bc.InstrW_W(asBC_CmpPtr, ctx->type.stackOffset, offset); + DeallocateVariable(offset); + + int afterLabel = nextLabel++; + ctx->bc.InstrDWORD(asBC_JZ, afterLabel); + + // Call the cast operator + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + ctx->bc.Instr(asBC_RDSPtr); + ctx->type.dataType.MakeReference(false); + + asCArray args; + MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + ctx->bc.Instr(asBC_PopPtr); + + int endLabel = nextLabel++; + + ctx->bc.InstrINT(asBC_JMP, endLabel); + ctx->bc.Label((short)afterLabel); + + // Make a NULL pointer + ctx->bc.InstrSHORT(asBC_ClrVPtr, ctx->type.stackOffset); + ctx->bc.Label((short)endLabel); + + // Push the reference to the handle on the stack + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + } + else + { + // Value types cannot be null, so there is no need to check for this. + + // Likewise for reference types that are registered with asOBJ_NOHANDLE + // as those are only expected as registered global properties that cannot + // be modified anyway. + + // Call the cast operator + asCArray args; + MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + } + } + else + { + asCScriptFunction *func = engine->scriptFunctions[ops[0]]; + ctx->type.Set(func->returnType); + } + } + else if( ops.GetLength() == 0 && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) ) + { + // Check for the generic ref cast method: void opCast(?&out) + for( n = 0; ot && n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( (isExplicit && func->name == "opCast") || + func->name == "opImplCast" ) + { + // Does the operator take the ?&out parameter? + if( func->returnType.GetTokenType() != ttVoid || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + ops.PushLast(func->id); + } + } + + // It shouldn't be possible to have more than one + // TODO: Should be allowed to have different implementations for const and non-const references + asASSERT( ops.GetLength() <= 1 ); + + if( ops.GetLength() == 1 ) + { + conversionDone = true; + if( generateCode ) + { + asASSERT(to.IsObjectHandle()); + + int afterLabel = 0; + bool doNullCheck = false; + asCExprContext tmp(engine); + if ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_NOHANDLE)) + { + tmp.bc.AddCode(&ctx->bc); + tmp.Merge(ctx); + + // Add code to avoid calling the cast behaviour if the handle is already null, + // because that will raise a null pointer exception due to the cast behaviour + // being a class method, and the this pointer cannot be null. + doNullCheck = true; + if (!ctx->type.isVariable) + { + Dereference(&tmp, true); + ConvertToVariable(&tmp); + } + + // The reference on the stack will not be used + tmp.bc.Instr(asBC_PopPtr); + + // TODO: runtime optimize: should have immediate comparison for null pointer + int offset = AllocateVariable(asCDataType::CreateNullHandle(), true); + // TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though) + tmp.bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset); + tmp.bc.InstrW_W(asBC_CmpPtr, tmp.type.stackOffset, offset); + DeallocateVariable(offset); + + afterLabel = nextLabel++; + tmp.bc.InstrDWORD(asBC_JZ, afterLabel); + + // Place the object pointer on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)tmp.type.stackOffset); + } + + // Allocate a temporary variable of the requested handle type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(true); + asCArray args; + asCExprContext arg(engine); + arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.type.isExplicitHandle = true; + args.PushLast(&arg); + + // Call the behaviour method + MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + + if (doNullCheck) + { + // Add the call after the null check + tmp.bc.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmp.bc); + + int endLabel = nextLabel++; + + ctx->bc.InstrINT(asBC_JMP, endLabel); + ctx->bc.Label((short)afterLabel); + + // Make a NULL pointer + ctx->bc.InstrSHORT(asBC_ClrVPtr, (short)stackOffset); + ctx->bc.Label((short)endLabel); + } + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + ctx->type.SetVariable(toRef, stackOffset, true); + ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset); + } + else + { + // All casts are legal + ctx->type.Set(to); + } + } + } + + // If the script object didn't implement a matching opCast or opImplCast + // then check if the desired type is part of the hierarchy + if( !conversionDone && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) ) + { + // We need it to be a reference + if( !ctx->type.dataType.IsReference() ) + { + asCDataType toRef = ctx->type.dataType; + toRef.MakeReference(true); + ImplicitConversion(ctx, toRef, 0, isExplicit ? asIC_EXPLICIT_REF_CAST : asIC_IMPLICIT_CONV, generateCode); + } + + if( isExplicit ) + { + // Allow dynamic cast between object handles (only for script objects). + // At run time this may result in a null handle, + // which when used will throw an exception + conversionDone = true; + if( generateCode ) + { + ctx->bc.InstrDWORD(asBC_Cast, engine->GetTypeIdFromDataType(to)); + + // Allocate a temporary variable for the returned object + int returnOffset = AllocateVariable(to, true); + + // Move the pointer from the object register to the temporary variable + ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); + + ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + ctx->type.SetVariable(to, returnOffset, true); + ctx->type.dataType.MakeReference(true); + } + else + { + ctx->type.dataType = to; + ctx->type.dataType.MakeReference(true); + } + } + else + { + if( CastToObjectType(ctx->type.dataType.GetTypeInfo())->DerivesFrom(to.GetTypeInfo()) ) + { + conversionDone = true; + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + } + + // A ref cast must not remove the constness + if( isConst ) + ctx->type.dataType.MakeHandleToConst(true); + } + + return conversionDone; +} + +asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asCExprContext *ctx, const asCDataType &toOrig, asCScriptNode *node, EImplicitConv convType, bool generateCode) +{ + asCDataType to = toOrig; + to.MakeReference(false); + asASSERT( !ctx->type.dataType.IsReference() ); + + // Maybe no conversion is needed + if( to.IsEqualExceptConst(ctx->type.dataType) ) + { + // A primitive is const or not + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + return asCC_NO_CONV; + } + + // Is the conversion an ambiguous enum value? + if( ctx->enumValue != "" ) + { + if( to.IsEnumType() ) + { + // Attempt to resolve an ambiguous enum value + asCDataType out; + asDWORD value; + if( builder->GetEnumValueFromType(CastToEnumType(to.GetTypeInfo()), ctx->enumValue.AddressOf(), out, value) ) + { + ctx->type.SetConstantDW(out, value); + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + + // Reset the enum value since we no longer need it + ctx->enumValue = ""; + + // It wasn't really a conversion. The compiler just resolved the ambiguity (or not) + return asCC_NO_CONV; + } + } + + // The enum value is ambiguous + if( node && generateCode ) + Error(TXT_FOUND_MULTIPLE_ENUM_VALUES, node); + + // Set a dummy to allow the compiler to try to continue the conversion + ctx->type.SetDummy(); + } + + // Determine the cost of this conversion + asUINT cost = asCC_NO_CONV; + if( (to.IsIntegerType() || to.IsUnsignedType()) && (ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) ) + cost = asCC_INT_FLOAT_CONV; + else if ((to.IsFloatType() || to.IsDoubleType()) && (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType())) + cost = asCC_INT_FLOAT_CONV; + else if (ctx->type.dataType.IsEnumType() && to.IsIntegerType() && to.GetSizeInMemoryBytes() == ctx->type.dataType.GetSizeInMemoryBytes() ) + cost = asCC_ENUM_SAME_SIZE_CONV; + else if (ctx->type.dataType.IsEnumType() && to.IsIntegerType() && to.GetSizeInMemoryBytes() != ctx->type.dataType.GetSizeInMemoryBytes()) + cost = asCC_ENUM_DIFF_SIZE_CONV; + else if( to.IsUnsignedType() && ctx->type.dataType.IsIntegerType() ) + cost = asCC_SIGNED_CONV; + else if( to.IsIntegerType() && ctx->type.dataType.IsUnsignedType() ) + cost = asCC_SIGNED_CONV; + else if( to.GetSizeInMemoryBytes() != ctx->type.dataType.GetSizeInMemoryBytes() ) + cost = asCC_PRIMITIVE_SIZE_CONV; + + // Start by implicitly converting constant values + if( ctx->type.isConstant ) + { + ImplicitConversionConstant(ctx, to, node, convType); + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + return cost; + } + + // Allow implicit conversion between numbers + if( generateCode ) + { + // When generating the code the decision has already been made, so we don't bother determining the cost + + // Convert smaller types to 32bit first + int s = ctx->type.dataType.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariable(ctx); + if( ctx->type.dataType.IsIntegerType() ) + { + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_sbTOi, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_swTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(ttInt); + } + else if( ctx->type.dataType.IsUnsignedType() ) + { + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_ubTOi, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_uwTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(ttUInt); + } + } + + if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1 && !to.IsEnumType()) || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_fTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_dTOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + + // Convert to smaller integer if necessary + s = to.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariable(ctx); + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset); + } + } + else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + if( ctx->type.dataType.IsUnsignedType() ) + ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_fTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_dTOi64, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_fTOu, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_dTOu, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + + // Convert to smaller integer if necessary + s = to.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariable(ctx); + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + if( ctx->type.dataType.IsUnsignedType() ) + ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_fTOu64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_dTOu64, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + + if( convType != asIC_EXPLICIT_VAL_CAST ) + Warning(TXT_FLOAT_CONV_TO_INT_CAUSE_TRUNC, node); + } + } + else if( to.IsFloatType() ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_iTOf, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_i64TOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_uTOf, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_u64TOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_dTOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( to.IsDoubleType() ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_iTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_i64TOd, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_uTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariable(ctx); + ctx->bc.InstrSHORT(asBC_u64TOd, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariable(ctx); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariable(to, true); + ctx->bc.InstrW_W(asBC_fTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + } + else + { + if( ((to.IsIntegerType() && !to.IsEnumType()) || to.IsUnsignedType() || + to.IsFloatType() || to.IsDoubleType() || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST)) && + (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType()) ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + } + } + + // Primitive types on the stack, can be const or non-const + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + return cost; +} + +asUINT asCCompiler::ImplicitConvLambdaToFunc(asCExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv /*convType*/, bool generateCode) +{ + asASSERT( to.IsFuncdef() && ctx->IsLambda() ); + + // Check that the lambda has the correct amount of arguments + asUINT count = 0; + asCScriptNode *argNode = ctx->exprNode->firstChild; + while( argNode->nodeType == snIdentifier ) + { + count++; + argNode = argNode->next; + } + asASSERT( argNode->nodeType == snStatementBlock ); + + asCScriptFunction *funcDef = CastToFuncdefType(to.GetTypeInfo())->funcdef; + if( funcDef->parameterTypes.GetLength() != count ) + return asCC_NO_CONV; + + // The Lambda can be used as this funcdef + ctx->type.dataType = to; + + if( generateCode ) + { + // Build a unique name for the anonymous function + asCString name; + if( m_globalVar ) + name.Format("$%s$%d", m_globalVar->name.AddressOf(), numLambdas++); + else + name.Format("$%s$%d", outFunc->GetDeclaration(), numLambdas++); + + // Register the lambda with the builder for later compilation + asCScriptFunction *func = builder->RegisterLambda(ctx->exprNode, script, funcDef, name, outFunc->nameSpace); + asASSERT( func == 0 || funcDef->IsSignatureExceptNameEqual(func) ); + ctx->bc.InstrPTR(asBC_FuncPtr, func); + + // Clear the expression node as it is no longer valid + ctx->exprNode = 0; + } + + return asCC_CONST_CONV; +} + +asUINT asCCompiler::ImplicitConversion(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct) +{ + asASSERT( ctx->type.dataType.GetTokenType() != ttUnrecognizedToken || + ctx->type.dataType.IsNullHandle() ); + + if( to.IsFuncdef() && ctx->IsLambda() ) + return ImplicitConvLambdaToFunc(ctx, to, node, convType, generateCode); + + // No conversion from void to any other type + if( ctx->type.dataType.GetTokenType() == ttVoid ) + return asCC_NO_CONV; + + // No conversion from class method to any type (it requires delegate) + if( ctx->IsClassMethod() ) + return asCC_NO_CONV; + + // Do we want a var type? + if( to.GetTokenType() == ttQuestion ) + { + // Any type can be converted to a var type, but only when not generating code + asASSERT( !generateCode ); + + ctx->type.dataType = to; + + return asCC_VARIABLE_CONV; + } + // Do we want a primitive? + else if( to.IsPrimitive() ) + { + if( !ctx->type.dataType.IsPrimitive() ) + return ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode); + else + return ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode); + } + else // The target is a complex type + { + if( ctx->type.dataType.IsPrimitive() ) + return ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, allowObjectConstruct); + else if( ctx->type.IsNullConstant() || ctx->type.dataType.GetTypeInfo() ) + return ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, allowObjectConstruct); + } + + return asCC_NO_CONV; +} + +asUINT asCCompiler::ImplicitConvObjectToPrimitive(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode) +{ + if( ctx->type.isExplicitHandle ) + { + // An explicit handle cannot be converted to a primitive + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + return asCC_NO_CONV; + } + + // TODO: Must use the const cast behaviour if the object is read-only + + // Find matching value cast behaviours + // Here we're only interested in those that convert the type to a primitive type + asCArray funcs; + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + if( ot == 0 ) + { + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + return asCC_NO_CONV; + } + + + if( convType == asIC_EXPLICIT_VAL_CAST ) + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + // accept both implicit and explicit cast + asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]]; + if( (mthd->name == "opConv" || mthd->name == "opImplConv") && + mthd->parameterTypes.GetLength() == 0 && + mthd->returnType.IsPrimitive() ) + funcs.PushLast(ot->methods[n]); + } + } + else + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + // accept only implicit cast + asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]]; + if( mthd->name == "opImplConv" && + mthd->parameterTypes.GetLength() == 0 && + mthd->returnType.IsPrimitive() ) + funcs.PushLast(ot->methods[n]); + } + } + + int funcId = 0; + if( to.IsMathType() ) + { + // This matrix describes the priorities of the types to search for, for each target type + // The first column is the target type, the priorities goes from left to right + eTokenType matchMtx[10][10] = + { + {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat}, + {ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat}, + }; + + // Which row to use? + eTokenType *row = 0; + for( unsigned int type = 0; type < 10; type++ ) + { + if( to.GetTokenType() == matchMtx[type][0] ) + { + row = &matchMtx[type][0]; + break; + } + } + + // Find the best matching cast operator + if( row ) + { + asCDataType target(to); + + // Priority goes from left to right in the matrix + for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ ) + { + target.SetTokenType(row[attempt]); + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); + if( descr->returnType.IsEqualExceptRefAndConst(target) ) + { + funcId = funcs[n]; + break; + } + } + } + } + } + else + { + // Only accept the exact conversion for non-math types + + // Find the matching cast operator + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); + if( descr->returnType.IsEqualExceptRefAndConst(to) ) + { + funcId = funcs[n]; + break; + } + } + } + + // Did we find a suitable function? + if( funcId != 0 ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + if( generateCode ) + { + Dereference(ctx, true); + PerformFunctionCall(funcId, ctx); + } + else + ctx->type.Set(descr->returnType); + + // Allow one more implicit conversion to another primitive type + return asCC_OBJ_TO_PRIMITIVE_CONV + ImplicitConversion(ctx, to, node, convType, generateCode, false); + } + + // TODO: clean-up: This part is similar to what is in ImplicitConvObjectValue + // If no direct conversion is found we should look for the generic form 'void opConv(?&out)' + funcs.SetLength(0); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") || + func->name == "opImplConv" ) + { + // Does the operator take the ?&out parameter? + if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + funcs.PushLast(ot->methods[n]); + } + } + + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + if( generateCode ) + { + // Allocate a temporary variable of the requested type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(true); + toRef.MakeReadOnly(false); + asCArray args; + asCExprContext arg(engine); + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.exprNode = node; + args.PushLast(&arg); + + // Call the behaviour method + MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + toRef.MakeReference(false); + ctx->type.SetVariable(toRef, stackOffset, true); + } + else + ctx->type.Set(to); + + return asCC_OBJ_TO_PRIMITIVE_CONV; + } + + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + return asCC_NO_CONV; +} + + +asUINT asCCompiler::ImplicitConvObjectRef(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode) +{ + // Convert null to any object type handle, but not to a non-handle type + if( ctx->type.IsNullConstant() && ctx->methodName == "" ) + { + if( to.IsObjectHandle() ) + { + ctx->type.dataType = to; + return asCC_REF_CONV; + } + return asCC_NO_CONV; + } + + asASSERT(ctx->type.dataType.GetTypeInfo() || ctx->methodName != ""); + + // First attempt to convert the base type without instantiating another instance + if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && ctx->methodName == "" ) + { + // If the to type is an interface and the from type implements it, then we can convert it immediately + if( ctx->type.dataType.GetTypeInfo()->Implements(to.GetTypeInfo()) ) + { + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + return asCC_REF_CONV; + } + // If the to type is a class and the from type derives from it, then we can convert it immediately + else if( ctx->type.dataType.GetTypeInfo()->DerivesFrom(to.GetTypeInfo()) ) + { + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + return asCC_REF_CONV; + } + // If the types are not equal yet, then we may still be able to find a reference cast + else if( ctx->type.dataType.GetTypeInfo() != to.GetTypeInfo() ) + { + // We may still be able to find an implicit ref cast behaviour + CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode); + + // Was the conversion done? + if( ctx->type.dataType.GetTypeInfo() == to.GetTypeInfo() ) + return asCC_REF_CONV; + } + } + + // Convert matching function types + if( to.IsFuncdef() ) + { + // If the input expression is already a funcdef, check if it can be converted + if( ctx->type.dataType.IsFuncdef() && + to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() ) + { + asCScriptFunction *toFunc = CastToFuncdefType(to.GetTypeInfo())->funcdef; + asCScriptFunction *fromFunc = CastToFuncdefType(ctx->type.dataType.GetTypeInfo())->funcdef; + if( toFunc->IsSignatureExceptNameEqual(fromFunc) ) + { + ctx->type.dataType.SetTypeInfo(to.GetTypeInfo()); + return asCC_REF_CONV; + } + } + + // If the input expression is a deferred function ref, check if there is a matching func + if( ctx->methodName != "" ) + { + // Determine the namespace + asSNameSpace *ns = 0; + asCString name = ""; + int pos = ctx->methodName.FindLast("::"); + if( pos >= 0 ) + { + asCString nsName = ctx->methodName.SubString(0, pos+2); + // Trim off the last :: + if( nsName.GetLength() > 2 ) + nsName.SetLength(nsName.GetLength()-2); + ns = DetermineNameSpace(nsName); + name = ctx->methodName.SubString(pos+2); + } + else + { + DetermineNameSpace(""); + name = ctx->methodName; + } + + asCArray funcs; + if( ns ) + builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); + + // Check if any of the functions have perfect match + asCScriptFunction *toFunc = CastToFuncdefType(to.GetTypeInfo())->funcdef; + for( asUINT n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *func = builder->GetFunctionDescription(funcs[n]); + if( toFunc->IsSignatureExceptNameEqual(func) ) + { + if( generateCode ) + { + ctx->bc.InstrPTR(asBC_FuncPtr, func); + + // Make sure the identified function is shared if we're compiling a shared function + if( !func->IsShared() && outFunc->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, func->GetDeclaration()); + Error(msg, node); + } + } + + ctx->type.dataType = asCDataType::CreateType(to.GetTypeInfo(), false); + return asCC_REF_CONV; + } + } + } + } + + return asCC_NO_CONV; +} + +asUINT asCCompiler::ImplicitConvObjectValue(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode) +{ + asUINT cost = asCC_NO_CONV; + + // If the base type is still different, and we are allowed to instance + // another object then we can try an implicit value cast + if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() ) + { + // TODO: Implement support for implicit constructor/factory + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + if( ot == 0 ) + return cost; + + asCArray funcs; + if( convType == asIC_EXPLICIT_VAL_CAST ) + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + + // accept both implicit and explicit cast + if( (func->name == "opConv" || + func->name == "opImplConv") && + func->returnType.GetTypeInfo() == to.GetTypeInfo() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); + } + } + else + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + + // accept only implicit cast + if( func->name == "opImplConv" && + func->returnType.GetTypeInfo() == to.GetTypeInfo() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); + } + } + + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]); + if( generateCode ) + { + Dereference(ctx, true); + + bool useVariable = false; + int stackOffset = 0; + + if( f->DoesReturnOnStack() ) + { + useVariable = true; + stackOffset = AllocateVariable(f->returnType, true); + + // Push the pointer to the pre-allocated space for the return value + ctx->bc.InstrSHORT(asBC_PSF, short(stackOffset)); + + // The object pointer is already on the stack, but should be the top + // one, so we need to swap the pointers in order to get the correct + ctx->bc.Instr(asBC_SwapPtr); + } + + PerformFunctionCall(funcs[0], ctx, false, 0, 0, useVariable, stackOffset); + } + else + ctx->type.Set(f->returnType); + + cost = asCC_TO_OBJECT_CONV; + } + else + { + // TODO: cleanup: This part is similar to the second half of ImplicitConvObjectToPrimitive + // Look for a value cast with variable type + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") || + func->name == "opImplConv" ) + { + // Does the operator take the ?&out parameter? + if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + funcs.PushLast(ot->methods[n]); + } + } + + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + cost = asCC_TO_OBJECT_CONV; + if( generateCode ) + { + // Allocate a temporary variable of the requested type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(false); + asCExprContext arg(engine); + arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); + + // If this an object on the heap, the pointer must be dereferenced + if( IsVariableOnHeap(stackOffset) ) + arg.bc.Instr(asBC_RDSPtr); + + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.exprNode = node; + + // Mark the argument as clean, so that MakeFunctionCall knows it + // doesn't have to make a copy of it in order to protect the value + arg.isCleanArg = true; + + // Call the behaviour method + asCArray args; + args.PushLast(&arg); + MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + ctx->type.SetVariable(toRef, stackOffset, true); + ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset); + } + else + { + // All casts are legal + ctx->type.Set(to); + } + } + } + } + + return cost; +} + +asUINT asCCompiler::ImplicitConvObjectToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct) +{ + // First try a ref cast + asUINT cost = ImplicitConvObjectRef(ctx, to, node, convType, generateCode); + + // If the desired type is an asOBJ_ASHANDLE then we'll assume it is allowed to implicitly + // construct the object through any of the available constructors + if( to.GetTypeInfo() && (to.GetTypeInfo()->flags & asOBJ_ASHANDLE) && to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && allowObjectConstruct ) + { + asCArray funcs; + funcs = CastToObjectType(to.GetTypeInfo())->beh.constructors; + + asCArray args; + args.PushLast(ctx); + + cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, 0, false, true, false); + + // Did we find a matching constructor? + if( funcs.GetLength() == 1 ) + { + if( generateCode ) + { + // If the ASHANDLE receives a variable type parameter, then we need to + // make sure the expression is treated as a handle and not as a value + asCScriptFunction *func = engine->scriptFunctions[funcs[0]]; + if( func->parameterTypes[0].GetTokenType() == ttQuestion ) + { + if( !ctx->type.isExplicitHandle ) + { + asCDataType toHandle = ctx->type.dataType; + toHandle.MakeHandle(true); + toHandle.MakeReference(true); + toHandle.MakeHandleToConst(ctx->type.dataType.IsReadOnly()); + ImplicitConversion(ctx, toHandle, node, asIC_IMPLICIT_CONV, true, false); + + asASSERT( ctx->type.dataType.IsObjectHandle() ); + } + ctx->type.isExplicitHandle = true; + } + + // TODO: This should really reuse the code from CompileConstructCall + + // Allocate the new object + asCExprValue tempObj; + tempObj.dataType = to; + tempObj.dataType.MakeReference(false); + tempObj.stackOffset = (short)AllocateVariable(tempObj.dataType, true); + tempObj.dataType.MakeReference(true); + tempObj.isTemporary = true; + tempObj.isVariable = true; + + bool onHeap = IsVariableOnHeap(tempObj.stackOffset); + + // Push the address of the object on the stack + asCExprContext e(engine); + if( onHeap ) + e.bc.InstrSHORT(asBC_VAR, tempObj.stackOffset); + + PrepareFunctionCall(funcs[0], &e.bc, args); + MoveArgsToStack(funcs[0], &e.bc, args, false); + + // If the object is allocated on the stack, then call the constructor as a normal function + if( onHeap ) + { + int offset = 0; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]); + offset = descr->parameterTypes[0].GetSizeOnStackDWords(); + + e.bc.InstrWORD(asBC_GETREF, (asWORD)offset); + } + else + e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + + PerformFunctionCall(funcs[0], &e, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo())); + + // Add tag that the object has been initialized + e.bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT); + + // The constructor doesn't return anything, + // so we have to manually inform the type of + // the return value + e.type = tempObj; + if( !onHeap ) + e.type.dataType.MakeReference(false); + + // Push the address of the object on the stack again + e.bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + + MergeExprBytecodeAndType(ctx, &e); + } + else + { + ctx->type.Set(asCDataType::CreateType(to.GetTypeInfo(), false)); + } + } + } + + // If the base type is still different, and we are allowed to instance + // another object then we can try an implicit value cast + if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() && allowObjectConstruct ) + { + // Attempt implicit value cast + cost = ImplicitConvObjectValue(ctx, to, node, convType, generateCode); + } + + // If we still haven't converted the base type to the correct type, then there is + // no need to continue as it is not possible to do the conversion + if( to.GetTypeInfo() != ctx->type.dataType.GetTypeInfo() ) + return asCC_NO_CONV; + + + if( to.IsObjectHandle() ) + { + // There is no extra cost in converting to a handle + + // reference to handle -> handle + // reference -> handle + // object -> handle + // handle -> reference to handle + // reference -> reference to handle + // object -> reference to handle + + // TODO: If the type is handle, then we can't use IsReadOnly to determine the constness of the basetype + + // If the rvalue is a handle to a const object, then + // the lvalue must also be a handle to a const object + if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() ) + { + if( convType != asIC_IMPLICIT_CONV ) + { + asASSERT(node); + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + } + + if( !ctx->type.dataType.IsObjectHandle() ) + { + // An object type can be directly converted to a handle of the + // same type by doing a ref copy to a new variable + if( ctx->type.dataType.SupportHandles() ) + { + asCDataType dt = ctx->type.dataType; + dt.MakeHandle(true); + dt.MakeReference(false); + + if( generateCode ) + { + // If the expression is already a local variable, then it is not + // necessary to do a ref copy, as the ref objects on the stack are + // really handles, only the handles cannot be modified. + if( ctx->type.isVariable ) + { + bool isHandleToConst = ctx->type.dataType.IsReadOnly(); + ctx->type.dataType.MakeReadOnly(false); + ctx->type.dataType.MakeHandle(true); + ctx->type.dataType.MakeReadOnly(true); + ctx->type.dataType.MakeHandleToConst(isHandleToConst); + + if( to.IsReference() && !ctx->type.dataType.IsReference() ) + { + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + ctx->type.dataType.MakeReference(true); + } + else if( ctx->type.dataType.IsReference() ) + { + ctx->bc.Instr(asBC_RDSPtr); + ctx->type.dataType.MakeReference(false); + } + } + else + { + int offset = AllocateVariable(dt, true); + + if( ctx->type.dataType.IsReference() ) + ctx->bc.Instr(asBC_RDSPtr); + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if (dt.IsFuncdef()) + ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx->bc.InstrPTR(asBC_REFCPY, dt.GetTypeInfo()); + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + if( to.IsReference() ) + dt.MakeReference(true); + else + ctx->bc.Instr(asBC_RDSPtr); + + ctx->type.SetVariable(dt, offset, true); + } + } + else + ctx->type.dataType = dt; + + // When this conversion is done the expression is no longer an lvalue + ctx->type.isLValue = false; + } + } + + if( ctx->type.dataType.IsObjectHandle() ) + { + // A handle to non-const can be converted to a + // handle to const, but not the other way + if( to.IsHandleToConst() ) + ctx->type.dataType.MakeHandleToConst(true); + + // A const handle can be converted to a non-const + // handle and vice versa as the handle is just a value + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + } + + if( to.IsReference() && !ctx->type.dataType.IsReference() ) + { + if( generateCode ) + { + asASSERT( ctx->type.dataType.IsObjectHandle() ); + + // If the input type is a handle, then a simple ref copy is enough + bool isExplicitHandle = ctx->type.isExplicitHandle; + ctx->type.isExplicitHandle = ctx->type.dataType.IsObjectHandle(); + + // If the input type is read-only we'll need to temporarily + // remove this constness, otherwise the assignment will fail + bool typeIsReadOnly = ctx->type.dataType.IsReadOnly(); + ctx->type.dataType.MakeReadOnly(false); + + // If the object already is a temporary variable, then the copy + // doesn't have to be made as it is already a unique object + PrepareTemporaryVariable(node, ctx); + + ctx->type.dataType.MakeReadOnly(typeIsReadOnly); + ctx->type.isExplicitHandle = isExplicitHandle; + } + + // A non-reference can be converted to a reference, + // by putting the value in a temporary variable + ctx->type.dataType.MakeReference(true); + + // Since it is a new temporary variable it doesn't have to be const + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + } + else if( !to.IsReference() && ctx->type.dataType.IsReference() ) + { + Dereference(ctx, generateCode); + } + } + else // if( !to.IsObjectHandle() ) + { + if( !to.IsReference() ) + { + // reference to handle -> object + // handle -> object + // reference -> object + + // An implicit handle can be converted to an object by adding a check for null pointer + if( ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle ) + { + if( generateCode ) + { + if( ctx->type.dataType.IsReference() ) + { + // The pointer on the stack refers to the handle + ctx->bc.Instr(asBC_ChkRefS); + } + else + { + // The pointer on the stack refers to the object + ctx->bc.Instr(asBC_CHKREF); + } + } + + ctx->type.dataType.MakeHandle(false); + } + + // A const object can be converted to a non-const object through a copy + if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() && + allowObjectConstruct ) + { + // Does the object type allow a copy to be made? + if( ctx->type.dataType.CanBeCopied() ) + { + if( generateCode ) + { + // Make a temporary object with the copy + PrepareTemporaryVariable(node, ctx); + } + + // In case the object was already in a temporary variable, then the function + // didn't really do anything so we need to remove the constness here + ctx->type.dataType.MakeReadOnly(false); + + // Add the cost for the copy + cost += asCC_TO_OBJECT_CONV; + } + } + + if( ctx->type.dataType.IsReference() ) + { + // This may look strange, but a value type allocated on the stack is already + // correct, so nothing should be done other than remove the mark as reference. + // For types allocated on the heap, it is necessary to dereference the pointer + // that is currently on the stack + if( IsVariableOnHeap(ctx->type.stackOffset) ) + Dereference(ctx, generateCode); + else + ctx->type.dataType.MakeReference(false); + } + + // A non-const object can be converted to a const object directly + if( !ctx->type.dataType.IsReadOnly() && to.IsReadOnly() ) + { + ctx->type.dataType.MakeReadOnly(true); + } + } + else // if( to.IsReference() ) + { + // reference to handle -> reference + // handle -> reference + // object -> reference + + if( ctx->type.dataType.IsReference() ) + { + if( ctx->type.isExplicitHandle && ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) ) + { + // ASHANDLE objects are really value types, so explicit handle can be removed + ctx->type.isExplicitHandle = false; + ctx->type.dataType.MakeHandle(false); + } + + // A reference to a handle can be converted to a reference to an object + // by first reading the address, then verifying that it is not null + if( !to.IsObjectHandle() && ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle ) + { + ctx->type.dataType.MakeHandle(false); + if( generateCode ) + ctx->bc.Instr(asBC_ChkRefS); + } + + // A reference to a non-const can be converted to a reference to a const + if( to.IsReadOnly() ) + ctx->type.dataType.MakeReadOnly(true); + else if( ctx->type.dataType.IsReadOnly() && allowObjectConstruct ) + { + // A reference to a const can be converted to a reference to a + // non-const by copying the object to a temporary variable + ctx->type.dataType.MakeReadOnly(false); + + if( generateCode ) + { + // If the object already is a temporary variable, then the copy + // doesn't have to be made as it is already a unique object + PrepareTemporaryVariable(node, ctx); + } + + // Add the cost for the copy + cost += asCC_TO_OBJECT_CONV; + } + } + else // if( !ctx->type.dataType.IsReference() ) + { + // A non-reference handle can be converted to a non-handle reference by checking against null handle + if( ctx->type.dataType.IsObjectHandle() ) + { + bool readOnly = false; + if( ctx->type.dataType.IsHandleToConst() ) + readOnly = true; + + if( generateCode ) + { + if( ctx->type.isVariable ) + ctx->bc.InstrSHORT(asBC_ChkNullV, ctx->type.stackOffset); + else + ctx->bc.Instr(asBC_CHKREF); + } + ctx->type.dataType.MakeHandle(false); + ctx->type.dataType.MakeReference(true); + + // Make sure a handle to const isn't converted to non-const reference + if( readOnly ) + ctx->type.dataType.MakeReadOnly(true); + } + else + { + // A value type allocated on the stack is differentiated + // by it not being a reference. But it can be handled as + // reference by pushing the pointer on the stack + if( (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) && + (ctx->type.isVariable || ctx->type.isTemporary) && + !IsVariableOnHeap(ctx->type.stackOffset) ) + { + // Actually the pointer is already pushed on the stack in + // CompileVariableAccess, so we don't need to do anything else + } + else if( generateCode ) + { + // A non-reference can be converted to a reference, + // by putting the value in a temporary variable + + // If the input type is read-only we'll need to temporarily + // remove this constness, otherwise the assignment will fail + bool typeIsReadOnly = ctx->type.dataType.IsReadOnly(); + ctx->type.dataType.MakeReadOnly(false); + + // If the object already is a temporary variable, then the copy + // doesn't have to be made as it is already a unique object + PrepareTemporaryVariable(node, ctx); + + ctx->type.dataType.MakeReadOnly(typeIsReadOnly); + + // Add the cost for the copy + cost += asCC_TO_OBJECT_CONV; + } + + // This may look strange as the conversion was to make the expression a reference + // but a value type allocated on the stack is a reference even without the type + // being marked as such. + ctx->type.dataType.MakeReference(IsVariableOnHeap(ctx->type.stackOffset)); + } + + // TODO: If the variable is an object allocated on the stack the following is not true as the copy may not have been made + // Since it is a new temporary variable it doesn't have to be const + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + } + } + } + + return cost; +} + +asUINT asCCompiler::ImplicitConvPrimitiveToObject(asCExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool generateCode, bool /*allowObjectConstruct*/) +{ + // Reference types currently don't allow implicit conversion from primitive to object + // TODO: Allow implicit conversion to scoped reference types as they are supposed to appear like ordinary value types + asCObjectType *objType = CastToObjectType(to.GetTypeInfo()); + asASSERT( objType || CastToFuncdefType(to.GetTypeInfo()) ); + if( !objType || (objType->flags & asOBJ_REF) ) + return asCC_NO_CONV; + + // For value types the object must have a constructor that takes a single primitive argument either by value or as input reference + asCArray funcs; + for( asUINT n = 0; n < objType->beh.constructors.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[objType->beh.constructors[n]]; + if( func->parameterTypes.GetLength() == 1 && + func->parameterTypes[0].IsPrimitive() && + !(func->inOutFlags[0] & asTM_OUTREF) ) + { + funcs.PushLast(func->id); + } + } + + if( funcs.GetLength() == 0 ) + return asCC_NO_CONV; + + // Check if it is possible to choose a best match + asCExprContext arg(engine); + arg.type = ctx->type; + arg.exprNode = ctx->exprNode; // Use the same node for compiler messages + asCArray args; + args.PushLast(&arg); + asUINT cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, 0, 0, 0, objType, false, true, false); + if( funcs.GetLength() != 1 ) + return asCC_NO_CONV; + + if( !generateCode ) + { + ctx->type.Set(to); + return cost; + } + + // TODO: clean up: This part is similar to CompileConstructCall(). It should be put in a common function + + // Clear the type of ctx, as the type is moved to the arg + ctx->type.SetDummy(); + + // Value types and script types are allocated through the constructor + asCExprValue tempObj; + tempObj.dataType = to; + tempObj.stackOffset = (short)AllocateVariable(to, true); + tempObj.dataType.MakeReference(true); + tempObj.isTemporary = true; + tempObj.isVariable = true; + + bool onHeap = IsVariableOnHeap(tempObj.stackOffset); + + // Push the address of the object on the stack + if( onHeap ) + ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset); + + PrepareFunctionCall(funcs[0], &ctx->bc, args); + MoveArgsToStack(funcs[0], &ctx->bc, args, false); + + if( !(objType->flags & asOBJ_REF) ) + { + // If the object is allocated on the stack, then call the constructor as a normal function + if( onHeap ) + { + int offset = 0; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]); + for( asUINT n = 0; n < args.GetLength(); n++ ) + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + + ctx->bc.InstrWORD(asBC_GETREF, (asWORD)offset); + } + else + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + + PerformFunctionCall(funcs[0], ctx, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo())); + + // Add tag that the object has been initialized + ctx->bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT); + + // The constructor doesn't return anything, + // so we have to manually inform the type of + // the return value + ctx->type = tempObj; + if( !onHeap ) + ctx->type.dataType.MakeReference(false); + + // Push the address of the object on the stack again + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + } + else + { + asASSERT( objType->flags & asOBJ_SCOPED ); + + // Call the factory to create the reference type + PerformFunctionCall(funcs[0], ctx, false, &args); + } + + return cost; +} + +void asCCompiler::ImplicitConversionConstant(asCExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType) +{ + asASSERT(from->type.isConstant); + + // TODO: node should be the node of the value that is + // converted (not the operator that provokes the implicit + // conversion) + + // If the base type is correct there is no more to do + if( to.IsEqualExceptRefAndConst(from->type.dataType) ) return; + + // References cannot be constants + if( from->type.dataType.IsReference() ) return; + + if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1 && !to.IsEnumType()) || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) ) + { + if( from->type.dataType.IsFloatType() || + from->type.dataType.IsDoubleType() || + from->type.dataType.IsUnsignedType() || + from->type.dataType.IsIntegerType() ) + { + asCDataType targetDt; + if (to.IsEnumType()) + targetDt = to; + else + targetDt = asCDataType::CreatePrimitive(ttInt, true); + + // Transform the value + // Float constants can be implicitly converted to int + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.GetConstantF(); + int ic = int(fc); + + if( float(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantDW(targetDt, ic); + } + // Double constants can be implicitly converted to int + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.GetConstantD(); + int ic = int(fc); + + if( double(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantDW(targetDt, ic); + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Verify that it is possible to convert to signed without getting negative + if( from->type.dataType.GetSizeInMemoryBytes() == 4 && + int(from->type.GetConstantDW()) < 0 && + convType != asIC_EXPLICIT_VAL_CAST && + node != 0 ) + Warning(TXT_CHANGE_SIGN, node); + + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantDW(targetDt, from->type.GetConstantB()); + else if (from->type.dataType.GetSizeInMemoryBytes() == 2) + from->type.SetConstantDW(targetDt, from->type.GetConstantW()); + else + from->type.dataType = targetDt; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + if (asQWORD(from->type.GetConstantQW()) >> 31) + if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + // Convert to 32bit + from->type.SetConstantDW(targetDt, int(from->type.GetConstantQW())); + } + else if (from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2) + { + if (int(from->type.GetConstantQW()) != asINT64(from->type.GetConstantQW())) + if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + // Convert to 32bit + from->type.SetConstantDW(targetDt, int(from->type.GetConstantQW())); + } + else if (from->type.dataType.IsIntegerType() && + from->type.dataType.GetSizeInMemoryBytes() < 4) + { + // Convert to 32bit + if (from->type.dataType.GetSizeInMemoryBytes() == 1) + from->type.SetConstantDW(targetDt, (signed char)from->type.GetConstantB()); + else if (from->type.dataType.GetSizeInMemoryBytes() == 2) + from->type.SetConstantDW(targetDt, (short)from->type.GetConstantW()); + } + else + { + // Only int32 and enums should come here and as these are 32bit + // already nothing needs to be done except set the target type + asASSERT((from->type.dataType.GetTokenType() == ttInt || + from->type.dataType.IsEnumType()) && + from->type.dataType.GetSizeInMemoryBytes() == 4); + + from->type.dataType = targetDt; + } + } + + // Check if a downsize is necessary + if( to.IsIntegerType() && + from->type.dataType.IsIntegerType() && + from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() ) + { + // Verify if it is possible + if( to.GetSizeInMemoryBytes() == 1 ) + { + if( char(from->type.GetConstantDW()) != int(from->type.GetConstantDW()) ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.SetConstantB(asCDataType::CreatePrimitive(to.GetTokenType(), true), char(from->type.GetConstantDW())); + } + else if( to.GetSizeInMemoryBytes() == 2 ) + { + if( short(from->type.GetConstantDW()) != int(from->type.GetConstantDW()) ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.SetConstantW(asCDataType::CreatePrimitive(to.GetTokenType(), true), short(from->type.GetConstantDW())); + } + } + } + else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 ) + { + // Float constants can be implicitly converted to int + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.GetConstantF(); + asINT64 ic = asINT64(fc); + + if( float(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), ic); + } + // Double constants can be implicitly converted to int + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.GetConstantD(); + asINT64 ic = asINT64(fc); + + if( double(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), ic); + } + else if( from->type.dataType.IsUnsignedType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantW()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), from->type.GetConstantDW()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 8 ) + { + if( asINT64(from->type.GetConstantQW()) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + } + } + else if( from->type.dataType.IsIntegerType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (signed char)from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (short)from->type.GetConstantW()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), (int)from->type.GetConstantDW()); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 ) + { + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.GetConstantF(); + // Some compilers set the value to 0 when converting a negative float to unsigned int. + // To maintain a consistent behaviour across compilers we convert to int first. + asUINT uic = asUINT(int(fc)); + + if( float(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), uic); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.GetConstantD(); + // Some compilers set the value to 0 when converting a negative double to unsigned int. + // To maintain a consistent behaviour across compilers we convert to int first. + asUINT uic = asUINT(int(fc)); + + if( double(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), uic); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsIntegerType() ) + { + // Verify that it is possible to convert to unsigned without loosing negative + if( (from->type.dataType.GetSizeInMemoryBytes() > 4 && asINT64(from->type.GetConstantQW()) < 0) || + (from->type.dataType.GetSizeInMemoryBytes() == 4 && int(from->type.GetConstantDW()) < 0) || + (from->type.dataType.GetSizeInMemoryBytes() == 2 && short(from->type.GetConstantW()) < 0) || + (from->type.dataType.GetSizeInMemoryBytes() == 1 && char(from->type.GetConstantB()) < 0)) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + // Check if any data is lost + if( from->type.dataType.GetSizeInMemoryBytes() > 4 && (from->type.GetConstantQW() >> 32) != 0 && (from->type.GetConstantQW() >> 32) != 0xFFFFFFFF ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + } + + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (signed char)from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (short)from->type.GetConstantW()); + else if (from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (int)from->type.GetConstantDW()); + else + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), (int)(asINT64)from->type.GetConstantQW()); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsUnsignedType() && + from->type.dataType.GetSizeInMemoryBytes() < 4 ) + { + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), from->type.GetConstantW()); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsUnsignedType() && + from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() ) + { + // Verify if it is possible + if( to.GetSizeInMemoryBytes() == 1 ) + { + if( asBYTE(from->type.GetConstantDW()) != from->type.GetConstantDW() ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.SetConstantB(asCDataType::CreatePrimitive(to.GetTokenType(), true), asBYTE(from->type.GetConstantDW())); + } + else if( to.GetSizeInMemoryBytes() == 2 ) + { + if( asWORD(from->type.GetConstantDW()) != from->type.GetConstantDW()) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.SetConstantW(asCDataType::CreatePrimitive(to.GetTokenType(), true), asWORD(from->type.GetConstantDW())); + } + else if (to.GetSizeInMemoryBytes() == 4) + { + if( asDWORD(from->type.GetConstantQW()) != from->type.GetConstantQW()) + if (convType != asIC_EXPLICIT_VAL_CAST && node) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.SetConstantDW(asCDataType::CreatePrimitive(to.GetTokenType(), true), asDWORD(from->type.GetConstantQW())); + } + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.GetConstantF(); + // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers + asQWORD uic = asQWORD(asINT64(fc)); + +#if !defined(_MSC_VER) || _MSC_VER > 1200 // MSVC++ 6 + // MSVC6 doesn't support this conversion + if( float(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } +#endif + + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), uic); + } + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.GetConstantD(); + // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers + asQWORD uic = asQWORD(asINT64(fc)); + +#if !defined(_MSC_VER) || _MSC_VER > 1200 // MSVC++ 6 + // MSVC6 doesn't support this conversion + if( double(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } +#endif + + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), uic); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(signed char)from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(short)from->type.GetConstantW()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), (asINT64)(int)from->type.GetConstantDW()); + + // Verify that it is possible to convert to unsigned without loosing negative + if( asINT64(from->type.GetConstantQW()) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // Verify that it is possible to convert to unsigned without loosing negative + if( asINT64(from->type.GetConstantQW()) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + else if( from->type.dataType.IsUnsignedType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantB()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantW()); + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), from->type.GetConstantDW()); + } + } + else if( to.IsFloatType() ) + { + if( from->type.dataType.IsDoubleType() ) + { + double ic = from->type.GetConstantD(); + float fc = float(ic); + + from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + int ic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + ic = (signed char)from->type.GetConstantB(); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + ic = (short)from->type.GetConstantW(); + else + ic = (int)from->type.GetConstantDW(); + float fc = float(ic); + + if( int(fc) != ic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + float fc = float(asINT64(from->type.GetConstantQW())); + if( asINT64(fc) != asINT64(from->type.GetConstantQW()) ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + unsigned int uic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + uic = from->type.GetConstantB(); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + uic = from->type.GetConstantW(); + else + uic = from->type.GetConstantDW(); + float fc = float(uic); + + if( (unsigned int)(fc) != uic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + float fc = float((asINT64)from->type.GetConstantQW()); + + if( asQWORD(fc) != from->type.GetConstantQW()) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantF(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + } + else if( to.IsDoubleType() ) + { + if( from->type.dataType.IsFloatType() ) + { + float ic = from->type.GetConstantF(); + double fc = double(ic); + + from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + int ic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + ic = (signed char)from->type.GetConstantB(); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + ic = (short)from->type.GetConstantW(); + else + ic = (int)from->type.GetConstantDW(); + double fc = double(ic); + + if( int(fc) != ic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + double fc = double(asINT64(from->type.GetConstantQW())); + + if( asINT64(fc) != asINT64(from->type.GetConstantQW()) ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + unsigned int uic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + uic = from->type.GetConstantB(); + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + uic = from->type.GetConstantW(); + else + uic = from->type.GetConstantDW(); + double fc = double(uic); + + if( (unsigned int)(fc) != uic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + double fc = double((asINT64)from->type.GetConstantQW()); + + if( asQWORD(fc) != from->type.GetConstantQW()) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.SetConstantD(asCDataType::CreatePrimitive(to.GetTokenType(), true), fc); + } + } +} + +int asCCompiler::DoAssignment(asCExprContext *ctx, asCExprContext *lctx, asCExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, eTokenType op, asCScriptNode *opNode) +{ + // Don't allow any operators on expressions that take address of class method + // If methodName is set but the type is not an object, then it is a global function + if( lctx->methodName != "" || rctx->IsClassMethod() ) + { + Error(TXT_INVALID_OP_ON_METHOD, opNode); + return -1; + } + + // Implicit handle types should always be treated as handles in assignments + if (lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + lctx->type.dataType.MakeHandle(true); + lctx->type.isExplicitHandle = true; + } + + // If the left hand expression is a property accessor, then that should be used + // to do the assignment instead of the ordinary operator. The exception is when + // the property accessor is for a handle property, and the operation is a value + // assignment. + if( (lctx->property_get || lctx->property_set) && + !(lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle) ) + { + if( op != ttAssignment ) + { + // Generate the code for the compound assignment, i.e. get the value, apply operator, then set the value + return ProcessPropertyGetSetAccessor(ctx, lctx, rctx, op, opNode); + } + + // It is not allowed to do a handle assignment on a property + // accessor that doesn't take a handle in the set accessor. + if( lctx->property_set && lctx->type.isExplicitHandle ) + { + // set_opIndex has 2 arguments, where as normal setters have only 1 + asCArray& parameterTypes = + builder->GetFunctionDescription(lctx->property_set)->parameterTypes; + if( !parameterTypes[parameterTypes.GetLength() - 1].IsObjectHandle() ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, opNode); + + Error(TXT_HANDLE_ASSIGN_ON_NON_HANDLE_PROP, opNode); + return -1; + } + } + + MergeExprBytecodeAndType(ctx, lctx); + + return ProcessPropertySetAccessor(ctx, rctx, opNode); + } + else if( lctx->property_get && lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle ) + { + // Get the handle to the object that will be used for the value assignment + ProcessPropertyGetAccessor(lctx, opNode); + } + + if( lctx->type.dataType.IsPrimitive() ) + { + if( !lctx->type.isLValue ) + { + Error(TXT_NOT_LVALUE, lexpr); + return -1; + } + + if( op != ttAssignment ) + { + // Compute the operator before the assignment + asCExprValue lvalue = lctx->type; + + if( lctx->type.isTemporary && !lctx->type.isVariable ) + { + // The temporary variable must not be freed until the + // assignment has been performed. lvalue still holds + // the information about the temporary variable + lctx->type.isTemporary = false; + } + + asCExprContext o(engine); + CompileOperator(opNode, lctx, rctx, &o); + MergeExprBytecode(rctx, &o); + rctx->type = o.type; + + // Convert the rvalue to the right type and validate it + PrepareForAssignment(&lvalue.dataType, rctx, rexpr, false); + + MergeExprBytecode(ctx, rctx); + lctx->type = lvalue; + + // The lvalue continues the same, either it was a variable, or a reference in the register + } + else + { + // Convert the rvalue to the right type and validate it + PrepareForAssignment(&lctx->type.dataType, rctx, rexpr, false, lctx); + + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + } + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ctx->type = lctx->type; + } + else if( lctx->type.isExplicitHandle ) + { + if( !lctx->type.isLValue ) + { + Error(TXT_NOT_LVALUE, lexpr); + return -1; + } + + // Object handles don't have any compound assignment operators + if( op != ttAssignment ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, lexpr); + return -1; + } + + if( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) ) + { + // The object is a value type but that should be treated as a handle + + // Make sure the right hand value is a handle + if( !rctx->type.isExplicitHandle && + !(rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE)) ) + { + // Function names can be considered handles already + if( rctx->methodName == "" ) + { + asCDataType dt = rctx->type.dataType; + dt.MakeHandle(true); + dt.MakeReference(false); + + PrepareArgument(&dt, rctx, rexpr, true, asTM_INREF); + if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, rexpr); + return -1; + } + } + } + + if( CompileOverloadedDualOperator(opNode, lctx, rctx, false, ctx, true) ) + { + // An overloaded assignment operator was found (or a compilation error occured) + return 0; + } + + // The object must implement the opAssign method + asCString msg; + msg.Format(TXT_NO_APPROPRIATE_OPHNDLASSIGN_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(msg.AddressOf(), opNode); + return -1; + } + else + { + asCDataType dt = lctx->type.dataType; + dt.MakeReference(false); + + PrepareArgument(&dt, rctx, rexpr, false, asTM_INREF , true); + if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, rexpr); + return -1; + } + + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + + ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE); + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + ctx->type = lctx->type; + + // After the handle assignment the original handle is left on the stack + ctx->type.dataType.MakeReference(false); + } + } + else // if( lctx->type.dataType.IsObject() ) + { + // The lvalue reference may be marked as a temporary, if for example + // it was originated as a handle returned from a function. In such + // cases it must be possible to assign values to it anyway. + if( lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle ) + { + // Convert the handle to a object reference + asCDataType to; + to = lctx->type.dataType; + to.MakeHandle(false); + ImplicitConversion(lctx, to, lexpr, asIC_IMPLICIT_CONV); + lctx->type.isLValue = true; // Handle may not have been an lvalue, but the dereferenced object is + } + + // Check for overloaded assignment operator + if( CompileOverloadedDualOperator(opNode, lctx, rctx, false, ctx) ) + { + // An overloaded assignment operator was found (or a compilation error occured) + return 0; + } + + // No registered operator was found. In case the operation is a direct + // assignment and the rvalue is the same type as the lvalue, then we can + // still use the byte-for-byte copy to do the assignment + + if( op != ttAssignment ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, lexpr); + return -1; + } + + // If the left hand expression is simple, i.e. without any + // function calls or allocations of memory, then we can avoid + // doing a copy of the right hand expression (done by PrepareArgument). + // Instead the reference to the value can be placed directly on the + // stack. + // + // This optimization should only be done for value types, where + // the application developer is responsible for making the + // implementation safe against unwanted destruction of the input + // reference before the time. + bool simpleExpr = (lctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) && lctx->bc.IsSimpleExpression(); + + // Implicitly convert the rvalue to the type of the lvalue + bool needConversion = false; + if( !lctx->type.dataType.IsEqualExceptRefAndConst(rctx->type.dataType) ) + needConversion = true; + + if( !simpleExpr || needConversion ) + { + asCDataType dt = lctx->type.dataType; + dt.MakeReference(true); + dt.MakeReadOnly(true); + int r = PrepareArgument(&dt, rctx, rexpr, true, 1, !needConversion); + if( r < 0 ) + return -1; + if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, rexpr); + return -1; + } + } + else + { + // Process any property accessor first, before placing the final reference on the stack + ProcessPropertyGetAccessor(rctx, rexpr); + + if( rctx->type.dataType.IsReference() && (!(rctx->type.isVariable || rctx->type.isTemporary) || IsVariableOnHeap(rctx->type.stackOffset)) ) + rctx->bc.Instr(asBC_RDSPtr); + } + + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + + if( !simpleExpr || needConversion ) + { + if( (rctx->type.isVariable || rctx->type.isTemporary) ) + { + if( !IsVariableOnHeap(rctx->type.stackOffset) ) + // TODO: runtime optimize: Actually the reference can be pushed on the stack directly + // as the value allocated on the stack is guaranteed to be safe. + // The bytecode optimizer should be able to determine this and optimize away the VAR + GETREF + ctx->bc.InstrWORD(asBC_GETREF, AS_PTR_SIZE); + else + ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE); + } + } + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + ctx->type = lctx->type; + } + + return 0; +} + +int asCCompiler::CompileAssignment(asCScriptNode *expr, asCExprContext *ctx) +{ + asCScriptNode *lexpr = expr->firstChild; + if( lexpr->next ) + { + // Compile the two expression terms + asCExprContext lctx(engine), rctx(engine); + int rr = CompileAssignment(lexpr->next->next, &rctx); + int lr = CompileCondition(lexpr, &lctx); + + if( lr >= 0 && rr >= 0 ) + return DoAssignment(ctx, &lctx, &rctx, lexpr, lexpr->next->next, lexpr->next->tokenType, lexpr->next); + + // Since the operands failed, the assignment was not computed + ctx->type.SetDummy(); + return -1; + } + + return CompileCondition(lexpr, ctx); +} + +int asCCompiler::CompileCondition(asCScriptNode *expr, asCExprContext *ctx) +{ + asCExprValue ctype; + + // Compile the conditional expression + asCScriptNode *cexpr = expr->firstChild; + if( cexpr->next ) + { + //------------------------------- + // Compile the condition + asCExprContext e(engine); + int r = CompileExpression(cexpr, &e); + if( r < 0 ) + e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( e.type.dataType.GetTypeInfo() && (e.type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&e, asCDataType::CreatePrimitive(ttBool, false), cexpr, asIC_IMPLICIT_CONV); + + if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + { + Error(TXT_EXPR_MUST_BE_BOOL, cexpr); + e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + ctype = e.type; + + ProcessPropertyGetAccessor(&e, cexpr); + + if( e.type.dataType.IsReference() ) ConvertToVariable(&e); + ProcessDeferredParams(&e); + + //------------------------------- + // Compile the left expression + asCExprContext le(engine); + int lr = CompileAssignment(cexpr->next, &le); + + // Resolve any function names already + DetermineSingleFunc(&le, cexpr->next); + + //------------------------------- + // Compile the right expression + asCExprContext re(engine); + int rr = CompileAssignment(cexpr->next->next, &re); + DetermineSingleFunc(&re, cexpr->next->next); + + if( lr >= 0 && rr >= 0 ) + { + // Don't allow any operators on expressions that take address of class method + if( le.IsClassMethod() || re.IsClassMethod() ) + { + Error(TXT_INVALID_OP_ON_METHOD, expr); + return -1; + } + + ProcessPropertyGetAccessor(&le, cexpr->next); + ProcessPropertyGetAccessor(&re, cexpr->next->next); + + bool isExplicitHandle = le.type.isExplicitHandle || re.type.isExplicitHandle; + + // Allow a 0 or null in the first case to be implicitly converted to the second type + if( le.type.isConstant && le.type.GetConstantData() == 0 && le.type.dataType.IsIntegerType() ) + { + asCDataType to = re.type.dataType; + to.MakeReference(false); + to.MakeReadOnly(true); + ImplicitConversionConstant(&le, to, cexpr->next, asIC_IMPLICIT_CONV); + } + else if( le.type.IsNullConstant() ) + { + asCDataType to = re.type.dataType; + to.MakeHandle(true); + ImplicitConversion(&le, to, cexpr->next, asIC_IMPLICIT_CONV); + } + + // Allow either case to be converted to const @ if the other is const @ + if( (le.type.dataType.IsHandleToConst() && !le.type.IsNullConstant()) || (re.type.dataType.IsHandleToConst() && !re.type.dataType.IsNullHandle()) ) + { + le.type.dataType.MakeHandleToConst(true); + re.type.dataType.MakeHandleToConst(true); + } + + //--------------------------------- + // Output the byte code + int afterLabel = nextLabel++; + int elseLabel = nextLabel++; + + // If left expression is void, then we don't need to store the result + if( le.type.dataType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttVoid, false)) ) + { + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Added the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Add the left expression + MergeExprBytecode(ctx, &le); + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Add the right expression + ctx->bc.Label((short)elseLabel); + MergeExprBytecode(ctx, &re); + ctx->bc.Label((short)afterLabel); + + // Make sure both expressions have the same type + if( le.type.dataType != re.type.dataType ) + Error(TXT_BOTH_MUST_BE_SAME, expr); + + // Set the type of the result + ctx->type = le.type; + } + else if (le.type.IsNullConstant() && re.type.IsNullConstant()) + { + // Special case for when both results are 'null' + // TODO: Other expressions where both results are identical literal constants can probably also be handled this way + + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Load the result into the register, but ignore the value since both paths give the same response + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Return a null constant + ctx->bc.Instr(asBC_PshNull); + ctx->type.SetNullConstant(); + } + else + { + // Allow "(a ? b : c) = d;" and "return (a ? b : c);" (where the latter returns the reference) + // + // Restrictions for the condition to be used as lvalue: + // 1. both b and c must be of the same type and be lvalue references + // 2. neither of the expressions can have any deferred arguments + // that would have to be cleaned up after the reference + // 3. neither expression can be temporary + // + // If either expression is local, the resulting lvalue is not valid + // for return since it is not allowed to return references to local + // variables. + // + // The reference to the local variable must be loaded into the register, + // the resulting expression must not be considered as a local variable + // with a stack offset (i.e. it will not be allowed to use asBC_VAR) + + if( le.type.isLValue && re.type.isLValue && + le.deferredParams.GetLength() == 0 && re.deferredParams.GetLength() ==0 && + !le.type.isTemporary && !re.type.isTemporary && + le.type.dataType == re.type.dataType ) + { + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Add the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Start of the left expression + MergeExprBytecode(ctx, &le); + if( !le.type.dataType.IsReference() && le.type.isVariable ) + { + // Load the address of the variable into the register + ctx->bc.InstrSHORT(asBC_LDV, le.type.stackOffset); + } + + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Start of the right expression + ctx->bc.Label((short)elseLabel); + + MergeExprBytecode(ctx, &re); + if( !re.type.dataType.IsReference() && re.type.isVariable ) + { + // Load the address of the variable into the register + ctx->bc.InstrSHORT(asBC_LDV, re.type.stackOffset); + } + + ctx->bc.Label((short)afterLabel); + + // In case the options were to objects, it is necessary to dereference the pointer on + // the stack so it will point to the actual object, instead of the variable + if( le.type.dataType.IsReference() && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() ) + { + asASSERT( re.type.dataType.IsReference() && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); + + ctx->bc.Instr(asBC_RDSPtr); + } + + // The result is an lvalue + ctx->type.isLValue = true; + ctx->type.dataType = le.type.dataType; + if( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsObjectHandle() ) + ctx->type.dataType.MakeReference(true); + else + ctx->type.dataType.MakeReference(false); + + // It can't be a treated as a variable, since we don't know which one was used + ctx->type.isVariable = false; + ctx->type.isTemporary = false; + + // Must remember if the reference was to a local variable, since it must not be allowed to be returned + ctx->type.isRefToLocal = le.type.isVariable || le.type.isRefToLocal || re.type.isVariable || re.type.isRefToLocal; + } + else + { + // Allocate temporary variable and copy the result to that one + asCExprValue temp; + temp = le.type; + temp.dataType.MakeReference(false); + temp.dataType.MakeReadOnly(false); + + // Make sure the variable isn't used in any of the expressions, + // as it would be overwritten which may cause crashes or less visible bugs + int l = int(reservedVariables.GetLength()); + e.bc.GetVarsUsed(reservedVariables); + le.bc.GetVarsUsed(reservedVariables); + re.bc.GetVarsUsed(reservedVariables); + int offset = AllocateVariable(temp.dataType, true, false); + reservedVariables.SetLength(l); + + temp.SetVariable(temp.dataType, offset, true); + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryVariable() + + CallDefaultConstructor(temp.dataType, offset, IsVariableOnHeap(offset), &ctx->bc, expr); + + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Add the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Assign the result of the left expression to the temporary variable + asCExprValue rtemp; + rtemp = temp; + if( rtemp.dataType.IsObjectHandle() ) + rtemp.isExplicitHandle = true; + + PrepareForAssignment(&rtemp.dataType, &le, cexpr->next, true); + MergeExprBytecode(ctx, &le); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + } + asCExprValue result; + result = rtemp; + PerformAssignment(&result, &le.type, &ctx->bc, cexpr->next); + if( !result.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) + + // Release the old temporary variable + ReleaseTemporaryVariable(le.type, &ctx->bc); + + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Start of the right expression + ctx->bc.Label((short)elseLabel); + + // Copy the result to the same temporary variable + PrepareForAssignment(&rtemp.dataType, &re, cexpr->next, true); + MergeExprBytecode(ctx, &re); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + } + result = rtemp; + PerformAssignment(&result, &re.type, &ctx->bc, cexpr->next); + if( !result.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) + + // Release the old temporary variable + ReleaseTemporaryVariable(re.type, &ctx->bc); + + ctx->bc.Label((short)afterLabel); + + // Make sure both expressions have the same type + if( !le.type.dataType.IsEqualExceptConst(re.type.dataType) ) + Error(TXT_BOTH_MUST_BE_SAME, expr); + + // Set the temporary variable as output + ctx->type = rtemp; + ctx->type.isExplicitHandle = isExplicitHandle; + + if( !ctx->type.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->type.dataType.MakeReference(IsVariableOnHeap(offset)); + } + + // Make sure the output isn't marked as being a literal constant + ctx->type.isConstant = false; + } + } + } + else + { + ctx->type.SetDummy(); + return -1; + } + } + else + return CompileExpression(cexpr, ctx); + + return 0; +} + +int asCCompiler::CompileExpression(asCScriptNode *expr, asCExprContext *ctx) +{ + asASSERT(expr->nodeType == snExpression); + + // Convert to polish post fix, i.e: a+b => ab+ + asCArray postfix; + ConvertToPostFix(expr, postfix); + + // Compile the postfix formatted expression + return CompilePostFixExpression(&postfix, ctx); +} + +void asCCompiler::ConvertToPostFix(asCScriptNode *expr, asCArray &postfix) +{ + // The algorithm that I've implemented here is similar to + // Djikstra's Shunting Yard algorithm, though I didn't know it at the time. + // ref: http://en.wikipedia.org/wiki/Shunting-yard_algorithm + + // Count the nodes in order to preallocate the buffers + int count = 0; + asCScriptNode *node = expr->firstChild; + while( node ) + { + count++; + node = node->next; + } + + asCArray stackA(count); + asCArray &stackB = postfix; + stackB.Allocate(count, false); + + node = expr->firstChild; + while( node ) + { + int precedence = GetPrecedence(node); + + while( stackA.GetLength() > 0 && + precedence <= GetPrecedence(stackA[stackA.GetLength()-1]) ) + stackB.PushLast(stackA.PopLast()); + + stackA.PushLast(node); + + node = node->next; + } + + while( stackA.GetLength() > 0 ) + stackB.PushLast(stackA.PopLast()); +} + +int asCCompiler::CompilePostFixExpression(asCArray *postfix, asCExprContext *ctx) +{ + // Shouldn't send any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + // Set the context to a dummy type to avoid further + // errors in case the expression fails to compile + ctx->type.SetDummy(); + + // Evaluate the operands and operators + asCArray free; + asCArray expr; + int ret = 0; + for( asUINT n = 0; ret == 0 && n < postfix->GetLength(); n++ ) + { + asCScriptNode *node = (*postfix)[n]; + if( node->nodeType == snExprTerm ) + { + asCExprContext *e = free.GetLength() ? free.PopLast() : asNEW(asCExprContext)(engine); + expr.PushLast(e); + e->exprNode = node; + ret = CompileExpressionTerm(node, e); + } + else + { + asCExprContext *r = expr.PopLast(); + asCExprContext *l = expr.PopLast(); + + // Now compile the operator + asCExprContext *e = free.GetLength() ? free.PopLast() : asNEW(asCExprContext)(engine); + ret = CompileOperator(node, l, r, e); + + expr.PushLast(e); + + // Free the operands + l->Clear(); + free.PushLast(l); + r->Clear(); + free.PushLast(r); + } + } + + if( ret == 0 ) + { + asASSERT(expr.GetLength() == 1); + + // The final result should be moved to the output context + MergeExprBytecodeAndType(ctx, expr[0]); + } + + // Clean up + for( asUINT e = 0; e < expr.GetLength(); e++ ) + asDELETE(expr[e], asCExprContext); + for( asUINT f = 0; f < free.GetLength(); f++ ) + asDELETE(free[f], asCExprContext); + + return ret; +} + +int asCCompiler::CompileExpressionTerm(asCScriptNode *node, asCExprContext *ctx) +{ + // Shouldn't send any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + // Check if this is an initialization of a temp object with an initialization list + if (node->firstChild && node->firstChild->nodeType == snDataType) + { + // TODO: It should be possible to infer the type of the object from where the + // expression will be used. The compilation of the initialization list + // should be deferred until it is known for what it will be used. It will + // then for example be possible to write expressions like: + // + // @dict = {{'key', 'value'}}; + // funcTakingArrayOfInt({1,2,3,4}); + + // Determine the type of the temporary object + asCDataType dt = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace); + + // Do not allow constructing non-shared types in shared functions + if (outFunc->IsShared() && + dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared()) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf()); + Error(msg, node); + } + + // Allocate and initialize the temporary object + int offset = AllocateVariable(dt, true); + CompileInitialization(node->lastChild, &ctx->bc, dt, node, offset, 0, 0); + + // Push the reference to the object on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->type.SetVariable(dt, offset, true); + ctx->type.isLValue = false; + + // If the variable is allocated on the heap we have a reference, + // otherwise the actual object pointer is pushed on the stack. + if (IsVariableOnHeap(offset)) + ctx->type.dataType.MakeReference(true); + + return 0; + } + + // Set the type as a dummy by default, in case of any compiler errors + ctx->type.SetDummy(); + + // Compile the value node + asCScriptNode *vnode = node->firstChild; + while( vnode->nodeType != snExprValue ) + vnode = vnode->next; + + asCExprContext v(engine); + int r = CompileExpressionValue(vnode, &v); if( r < 0 ) return r; + + // Compile post fix operators + asCScriptNode *pnode = vnode->next; + while( pnode ) + { + r = CompileExpressionPostOp(pnode, &v); if( r < 0 ) return r; + pnode = pnode->next; + } + + // Compile pre fix operators + pnode = vnode->prev; + while( pnode ) + { + r = CompileExpressionPreOp(pnode, &v); if( r < 0 ) return r; + pnode = pnode->prev; + } + + // Return the byte code and final type description + MergeExprBytecodeAndType(ctx, &v); + + return 0; +} + +int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &scope, asCExprContext *ctx, asCScriptNode *errNode, bool isOptional, bool noFunction, bool noGlobal, asCObjectType *objType) +{ + bool found = false; + + // It is a local variable or parameter? + // This is not accessible by default arg expressions + sVariable *v = 0; + if( !isCompilingDefaultArg && scope == "" && !objType && variables ) + v = variables->GetVariable(name.AddressOf()); + if( v ) + { + found = true; + + if( v->isPureConstant ) + ctx->type.SetConstantData(v->type, v->constantValue); + else if( v->type.IsPrimitive() ) + { + if( v->type.IsReference() ) + { + // Copy the reference into the register + ctx->bc.InstrSHORT(asBC_PshVPtr, (short)v->stackOffset); + ctx->bc.Instr(asBC_PopRPtr); + ctx->type.Set(v->type); + } + else + ctx->type.SetVariable(v->type, v->stackOffset, false); + + // Set as lvalue unless it is a const variable + if( !v->type.IsReadOnly() ) + ctx->type.isLValue = true; + } + else + { + ctx->bc.InstrSHORT(asBC_PSF, (short)v->stackOffset); + ctx->type.SetVariable(v->type, v->stackOffset, false); + + // If the variable is allocated on the heap we have a reference, + // otherwise the actual object pointer is pushed on the stack. + if( v->onHeap || v->type.IsObjectHandle() ) ctx->type.dataType.MakeReference(true); + + // Implicitly dereference handle parameters sent by reference + if( v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()) ) + ctx->bc.Instr(asBC_RDSPtr); + + // Mark the object as safe for access unless it is a handle, as the + // life time of the object is guaranteed throughout the scope. + if( !v->type.IsObjectHandle() ) + ctx->type.isHandleSafe = true; + + // Set as lvalue unless it is a const variable + if (!v->type.IsReadOnly()) + ctx->type.isLValue = true; + } + } + + // Is it a class member? + // This is not accessible by default arg expressions + if( !isCompilingDefaultArg && !found && ((objType) || (outFunc && outFunc->objectType && scope == "")) ) + { + if( name == THIS_TOKEN && !objType ) + { + asCDataType dt = asCDataType::CreateType(outFunc->objectType, outFunc->isReadOnly); + + // The object pointer is located at stack position 0 + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + ctx->type.isLValue = true; + + // The 'this' handle is always considered safe (i.e. life time guaranteed) + ctx->type.isHandleSafe = true; + + found = true; + } + + if( !found ) + { + // See if there are any matching property accessors + asCExprContext access(engine); + if( objType ) + access.type.Set(asCDataType::CreateType(objType, false)); + else + access.type.Set(asCDataType::CreateType(outFunc->objectType, outFunc->isReadOnly)); + access.type.dataType.MakeReference(true); + int r = 0; + if( errNode->next && errNode->next->tokenType == ttOpenBracket ) + { + // This is an index access, check if there is a property accessor that takes an index arg + asCExprContext dummyArg(engine); + r = FindPropertyAccessor(name, &access, &dummyArg, errNode, 0, true); + } + if( r == 0 ) + { + // Normal property access + r = FindPropertyAccessor(name, &access, errNode, 0, true); + } + if( r < 0 ) return -1; + if( access.property_get || access.property_set ) + { + if( !objType ) + { + // Prepare the bytecode for the member access + // This is only done when accessing through the implicit this pointer + ctx->bc.InstrSHORT(asBC_PSF, 0); + } + MergeExprBytecodeAndType(ctx, &access); + + found = true; + } + } + + if( !found ) + { + asCDataType dt; + if( objType ) + dt = asCDataType::CreateType(objType, false); + else + dt = asCDataType::CreateType(outFunc->objectType, false); + asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf()); + if( prop ) + { + // Is the property access allowed? + if( prop->isPrivate && prop->isInherited ) + { + if( engine->ep.privatePropAsProtected ) + { + // The application is allowing inherited classes to access private properties of the parent + // class. This option is allowed to provide backwards compatibility with pre-2.30.0 versions + // as it was how the compiler behaved earlier. + asCString msg; + msg.Format(TXT_ACCESSING_PRIVATE_PROP_s, name.AddressOf()); + Warning(msg, errNode); + } + else + { + asCString msg; + msg.Format(TXT_INHERITED_PRIVATE_PROP_ACCESS_s, name.AddressOf()); + Error(msg, errNode); + } + } + + if( !objType ) + { + // The object pointer is located at stack position 0 + // This is only done when accessing through the implicit this pointer + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + Dereference(ctx, true); + } + + // TODO: This is the same as what is in CompileExpressionPostOp + // Put the offset on the stack + ctx->bc.InstrSHORT_DW(asBC_ADDSi, (short)prop->byteOffset, engine->GetTypeIdFromDataType(dt)); + + if( prop->type.IsReference() ) + ctx->bc.Instr(asBC_RDSPtr); + + // Reference to primitive must be stored in the temp register + if( prop->type.IsPrimitive() ) + { + // TODO: runtime optimize: The ADD offset command should store the reference in the register directly + ctx->bc.Instr(asBC_PopRPtr); + } + + // Set the new type (keeping info about temp variable) + ctx->type.dataType = prop->type; + ctx->type.dataType.MakeReference(true); + ctx->type.isVariable = false; + ctx->type.isLValue = true; + + if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() ) + { + // Objects that are members are not references + ctx->type.dataType.MakeReference(false); + + // Objects that are members but not handles are safe as long as the parent object is safe + if (!objType || ctx->type.isHandleSafe) + ctx->type.isHandleSafe = true; + } + else if (ctx->type.dataType.IsObjectHandle()) + { + // Objects accessed through handles cannot be considered safe + // as the handle can be cleared at any time + ctx->type.isHandleSafe = false; + } + + // If the object reference is const, the property will also be const + ctx->type.dataType.MakeReadOnly(outFunc->isReadOnly); + + found = true; + } + else if( outFunc->objectType ) + { + // If it is not a property, it may still be the name of a method which can be used to create delegates + asCObjectType *ot = outFunc->objectType; + asCScriptFunction *func = 0; + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]]; + if( f->name == name && + (builder->module->accessMask & f->accessMask) ) + { + func = f; + break; + } + } + + if( func ) + { + // An object method was found. Keep the name of the method in the expression, but + // don't actually modify the bytecode at this point since it is not yet known what + // the method will be used for, or even what overloaded method should be used. + ctx->methodName = name; + + // Place the object pointer on the stack, as if the expression was this.func + if( !objType ) + { + // The object pointer is located at stack position 0 + // This is only done when accessing through the implicit this pointer + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(asCDataType::CreateType(outFunc->objectType, false), 0, false); + ctx->type.dataType.MakeReference(true); + Dereference(ctx, true); + } + + found = true; + } + } + } + } + + // Recursively search parent namespaces for global entities + asCString currScope = scope; + + // Get the namespace for this scope. This may return null if the scope is an enum + asSNameSpace *ns = DetermineNameSpace(currScope); + if( ns && currScope != "::" ) + currScope = ns->name; + + while( !found && !noGlobal && !objType ) + { + // Is it a global property? + if( !found && ns ) + { + // See if there are any matching global property accessors + asCExprContext access(engine); + int r = 0; + if( errNode->next && errNode->next->tokenType == ttOpenBracket ) + { + // This is an index access, check if there is a property accessor that takes an index arg + asCExprContext dummyArg(engine); + r = FindPropertyAccessor(name, &access, &dummyArg, errNode, ns); + } + if( r == 0 ) + { + // Normal property access + r = FindPropertyAccessor(name, &access, errNode, ns); + } + if( r < 0 ) return -1; + if( access.property_get || access.property_set ) + { + // Prepare the bytecode for the function call + MergeExprBytecodeAndType(ctx, &access); + + found = true; + } + + // See if there is any matching global property + if( !found ) + { + bool isCompiled = true; + bool isPureConstant = false; + bool isAppProp = false; + asQWORD constantValue = 0; + asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), ns, &isCompiled, &isPureConstant, &constantValue, &isAppProp); + if( prop ) + { + found = true; + + // Verify that the global property has been compiled already + if( isCompiled ) + { + if( ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + ctx->type.dataType.MakeHandle(true); + ctx->type.isExplicitHandle = true; + } + + // If the global property is a pure constant + // we can allow the compiler to optimize it. Pure + // constants are global constant variables that were + // initialized by literal constants. + if (isPureConstant) + ctx->type.SetConstantData(prop->type, constantValue); + else + { + // A shared type must not access global vars, unless they + // too are shared, e.g. application registered vars + if( outFunc->IsShared() ) + { + if( !isAppProp ) + { + asCString str; + str.Format(TXT_SHARED_CANNOT_ACCESS_NON_SHARED_VAR_s, prop->name.AddressOf()); + Error(str, errNode); + + // Allow the compilation to continue to catch other problems + } + } + + ctx->type.Set(prop->type); + ctx->type.isLValue = true; + + if( ctx->type.dataType.IsPrimitive() ) + { + // Load the address of the variable into the register + ctx->bc.InstrPTR(asBC_LDG, prop->GetAddressOfValue()); + + ctx->type.dataType.MakeReference(true); + } + else + { + // Push the address of the variable on the stack + ctx->bc.InstrPTR(asBC_PGA, prop->GetAddressOfValue()); + + // If the object is a value type or a non-handle variable to a reference type, + // then we must validate the existance as it could potentially be accessed + // before it is initialized. + // This check is not needed for application registered properties, since they + // are guaranteed to be valid by the application itself. + if( !isAppProp && + ((ctx->type.dataType.GetTypeInfo()->flags & asOBJ_VALUE) || + !ctx->type.dataType.IsObjectHandle()) ) + { + ctx->bc.Instr(asBC_ChkRefS); + } + + // If the address pushed on the stack is to a value type or an object + // handle, then mark the expression as a reference. Addresses to a reference + // type aren't marked as references to get correct behaviour + if( (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_VALUE) || + ctx->type.dataType.IsObjectHandle() ) + { + ctx->type.dataType.MakeReference(true); + } + else + { + asASSERT( (ctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !ctx->type.dataType.IsObjectHandle() ); + + // It's necessary to dereference the pointer so the pointer on the stack will point to the actual object + ctx->bc.Instr(asBC_RDSPtr); + } + } + } + } + else + { + asCString str; + str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, prop->name.AddressOf()); + Error(str, errNode); + return -1; + } + } + } + } + + // Is it the name of a global function? + if( !noFunction && !found && ns ) + { + asCArray funcs; + + builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); + + if( funcs.GetLength() > 0 ) + { + found = true; + + // Defer the evaluation of which function until it is actually used + // Store the namespace and name of the function for later + ctx->type.SetUndefinedFuncHandle(engine); + ctx->methodName = ns ? ns->name + "::" + name : name; + } + } + + // Is it an enum value? + if( !found ) + { + // The enum type may be declared in a namespace too + asCTypeInfo *scopeType = 0; + if( currScope != "" && currScope != "::" ) + { + builder->GetNameSpaceByString(currScope, outFunc->objectType ? outFunc->objectType->nameSpace : outFunc->nameSpace, errNode, script, &scopeType, false); + if (CastToEnumType(scopeType) == 0) + scopeType = 0; + } + + asDWORD value = 0; + asCDataType dt; + if( scopeType && builder->GetEnumValueFromType(CastToEnumType(scopeType), name.AddressOf(), dt, value) ) + { + // scoped enum value found + found = true; + } + else if( !engine->ep.requireEnumScope ) + { + // Look for the enum value without explicitly informing the enum type + asSNameSpace *nsEnum = DetermineNameSpace(currScope); + int e = 0; + if(nsEnum) + e = builder->GetEnumValue(name.AddressOf(), dt, value, nsEnum); + if( e ) + { + found = true; + if( e == 2 ) + { + // Ambiguous enum value: Save the name for resolution later. + // The ambiguity could be resolved now, but I hesitate + // to store too much information in the context. + ctx->enumValue = name.AddressOf(); + + // We cannot set a dummy value because it will pass through + // cleanly as an integer. + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttIdentifier, true), 0); + return 0; + } + } + } + + if( found ) + { + // Even if the enum type is not shared, and we're compiling a shared object, + // the use of the values are still allowed, since they are treated as constants. + + // an enum value was resolved + ctx->type.SetConstantDW(dt, value); + } + else + { + // If nothing was found because the scope doesn't match a namespace or an enum + // then this should be reported as an error and the search interrupted + if( !ns && !scopeType ) + { + ctx->type.SetDummy(); + asCString str; + str.Format(TXT_UNKNOWN_SCOPE_s, scope.AddressOf()); + Error(str, errNode); + return -1; + } + } + } + + if( !found ) + { + if( currScope == "" || currScope == "::" ) + break; + + // Move up to parent namespace + int pos = currScope.FindLast("::"); + if( pos >= 0 ) + currScope = currScope.SubString(0, pos); + else + currScope = "::"; + + if( ns ) + ns = engine->GetParentNameSpace(ns); + } + } + + // The name doesn't match any variable + if( !found ) + { + // Give dummy value + ctx->type.SetDummy(); + + if( !isOptional ) + { + // Prepend the scope to the name for the error message + asCString ename; + if( scope != "" && scope != "::" ) + ename = scope + "::"; + else + ename = scope; + ename += name; + + asCString str; + str.Format(TXT_s_NOT_DECLARED, ename.AddressOf()); + Error(str, errNode); + + // Declare the variable now so that it will not be reported again + variables->DeclareVariable(name.AddressOf(), asCDataType::CreatePrimitive(ttInt, false), 0x7FFF, true); + + // Mark the variable as initialized so that the user will not be bother by it again + v = variables->GetVariable(name.AddressOf()); + asASSERT(v); + if( v ) v->isInitialized = true; + } + + // Return -1 to signal that the variable wasn't found + return -1; + } + + return 0; +} + +int asCCompiler::CompileExpressionValue(asCScriptNode *node, asCExprContext *ctx) +{ + // Shouldn't receive any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + asCScriptNode *vnode = node->firstChild; + ctx->exprNode = vnode; + if( vnode->nodeType == snVariableAccess ) + { + // Determine the scope resolution of the variable + asCString scope = builder->GetScopeFromNode(vnode->firstChild, script, &vnode); + + // Determine the name of the variable + asASSERT(vnode->nodeType == snIdentifier ); + asCString name(&script->code[vnode->tokenPos], vnode->tokenLength); + + return CompileVariableAccess(name, scope, ctx, node); + } + else if( vnode->nodeType == snConstant ) + { + if( vnode->tokenType == ttIntConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + bool overflow = false; + asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0, &overflow); + + // Is the number bigger than a 64bit word? + if (overflow) + { + Error(TXT_VALUE_TOO_LARGE_FOR_TYPE, vnode); + + // Set the value to zero to avoid further warnings + val = 0; + } + + // Do we need 64 bits? + // If the 31st bit is set we'll treat the value as a signed 64bit number to avoid + // incorrect warnings about changing signs if the value is assigned to a 64bit variable + if( val>>31 ) + { + // Only if the value uses the last bit of a 64bit word do we consider the number unsigned + if( val>>63 ) + ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val); + else + ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttInt64, true), val); + } + else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), asDWORD(val)); + } + else if( vnode->tokenType == ttBitsConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + // Let the function determine the radix from the prefix 0x = 16, 0d = 10, 0o = 8, or 0b = 2 + bool overflow = false; + asQWORD val = asStringScanUInt64(value.AddressOf(), 0, 0, &overflow); + + // Is the number bigger than a 64bit word? + if (overflow) + { + Error(TXT_VALUE_TOO_LARGE_FOR_TYPE, vnode); + + // Set the value to zero to avoid further warnings + val = 0; + } + + // Do we need 64 bits? + if( val>>32 ) + ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val); + else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val)); + } + else if( vnode->tokenType == ttFloatConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + // TODO: Check for overflow + + size_t numScanned; + float v = float(asStringScanDouble(value.AddressOf(), &numScanned)); + ctx->type.SetConstantF(asCDataType::CreatePrimitive(ttFloat, true), v); +#ifndef AS_USE_DOUBLE_AS_FLOAT + // Don't check this if we have double as float, because then the whole token would be scanned (i.e. no f suffix) + asASSERT(numScanned == vnode->tokenLength - 1); +#endif + } + else if( vnode->tokenType == ttDoubleConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + // TODO: Check for overflow + + size_t numScanned; + double v = asStringScanDouble(value.AddressOf(), &numScanned); + ctx->type.SetConstantD(asCDataType::CreatePrimitive(ttDouble, true), v); + asASSERT(numScanned == vnode->tokenLength); + } + else if( vnode->tokenType == ttTrue || + vnode->tokenType == ttFalse ) + { +#if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0); +#else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + } + else if( vnode->tokenType == ttStringConstant || + vnode->tokenType == ttMultilineStringConstant || + vnode->tokenType == ttHeredocStringConstant ) + { + asCString str; + asCScriptNode *snode = vnode->firstChild; + if( script->code[snode->tokenPos] == '\'' && engine->ep.useCharacterLiterals ) + { + // Treat the single quoted string as a single character literal + str.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + + asDWORD val = 0; + if( str.GetLength() && (unsigned char)str[0] > 127 && engine->ep.scanner == 1 ) + { + // This is the start of a UTF8 encoded character. We need to decode it + val = asStringDecodeUTF8(str.AddressOf(), 0); + if( val == (asDWORD)-1 ) + Error(TXT_INVALID_CHAR_LITERAL, vnode); + } + else + { + val = ProcessStringConstant(str, snode); + if( val == (asDWORD)-1 ) + Error(TXT_INVALID_CHAR_LITERAL, vnode); + } + + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), val); + } + else + { + // Process the string constants + while( snode ) + { + asCString cat; + if( snode->tokenType == ttStringConstant ) + { + cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + ProcessStringConstant(cat, snode); + } + else if( snode->tokenType == ttMultilineStringConstant ) + { + if( !engine->ep.allowMultilineStrings ) + Error(TXT_MULTILINE_STRINGS_NOT_ALLOWED, snode); + + cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + ProcessStringConstant(cat, snode); + } + else if( snode->tokenType == ttHeredocStringConstant ) + { + cat.Assign(&script->code[snode->tokenPos+3], snode->tokenLength-6); + ProcessHeredocStringConstant(cat, snode); + } + + str += cat; + + snode = snode->next; + } + + // Call the string factory function to create a string object + asCScriptFunction *descr = engine->stringFactory; + if( descr == 0 ) + { + // Error + Error(TXT_STRINGS_NOT_RECOGNIZED, vnode); + + // Give dummy value + ctx->type.SetDummy(); + return -1; + } + else + { + // Register the constant string with the engine + int id = engine->AddConstantString(str.AddressOf(), str.GetLength()); + ctx->bc.InstrWORD(asBC_STR, (asWORD)id); + + bool useVariable = false; + int stackOffset = 0; + + if( descr->DoesReturnOnStack() ) + { + useVariable = true; + stackOffset = AllocateVariable(descr->returnType, true); + ctx->bc.InstrSHORT(asBC_PSF, short(stackOffset)); + } + + PerformFunctionCall(descr->id, ctx, false, 0, 0, useVariable, stackOffset); + } + } + } + else if( vnode->tokenType == ttNull ) + { + ctx->bc.Instr(asBC_PshNull); + ctx->type.SetNullConstant(); + } + else + asASSERT(false); + } + else if( vnode->nodeType == snFunctionCall ) + { + // Determine the scope resolution + asCString scope = builder->GetScopeFromNode(vnode->firstChild, script); + + return CompileFunctionCall(vnode, ctx, 0, false, scope); + } + else if( vnode->nodeType == snConstructCall ) + { + return CompileConstructCall(vnode, ctx); + } + else if( vnode->nodeType == snAssignment ) + { + asCExprContext e(engine); + int r = CompileAssignment(vnode, &e); + if( r < 0 ) + { + ctx->type.SetDummy(); + return r; + } + MergeExprBytecodeAndType(ctx, &e); + } + else if( vnode->nodeType == snCast ) + { + // Implement the cast operator + return CompileConversion(vnode, ctx); + } + else if( vnode->nodeType == snUndefined && vnode->tokenType == ttVoid ) + { + // This is a void expression + ctx->SetVoidExpression(); + } + else if( vnode->nodeType == snFunction ) + { + // This is an anonymous function + // Defer the evaluation of the function until it known where it + // will be used, which is where the signature will be defined + ctx->SetLambda(vnode); + } + else + asASSERT(false); + + return 0; +} + +asUINT asCCompiler::ProcessStringConstant(asCString &cstr, asCScriptNode *node, bool processEscapeSequences) +{ + int charLiteral = -1; + + // Process escape sequences + asCArray str((int)cstr.GetLength()); + + for( asUINT n = 0; n < cstr.GetLength(); n++ ) + { +#ifdef AS_DOUBLEBYTE_CHARSET + // Double-byte charset is only allowed for ASCII and not UTF16 encoded strings + if( (cstr[n] & 0x80) && engine->ep.scanner == 0 && engine->ep.stringEncoding != 1 ) + { + // This is the lead character of a double byte character + // include the trail character without checking it's value. + str.PushLast(cstr[n]); + n++; + str.PushLast(cstr[n]); + continue; + } +#endif + + asUINT val; + + if( processEscapeSequences && cstr[n] == '\\' ) + { + ++n; + if( n == cstr.GetLength() ) + { + if( charLiteral == -1 ) charLiteral = 0; + return charLiteral; + } + + // Hexadecimal escape sequences will allow the construction of + // invalid unicode sequences, but the string should also work as + // a bytearray so we must support this. The code for working with + // unicode text must be prepared to handle invalid unicode sequences + if( cstr[n] == 'x' || cstr[n] == 'X' ) + { + ++n; + if( n == cstr.GetLength() ) break; + + val = 0; + int c = engine->ep.stringEncoding == 1 ? 4 : 2; + for( ; c > 0 && n < cstr.GetLength(); c--, n++ ) + { + if( cstr[n] >= '0' && cstr[n] <= '9' ) + val = val*16 + cstr[n] - '0'; + else if( cstr[n] >= 'a' && cstr[n] <= 'f' ) + val = val*16 + cstr[n] - 'a' + 10; + else if( cstr[n] >= 'A' && cstr[n] <= 'F' ) + val = val*16 + cstr[n] - 'A' + 10; + else + break; + } + + // Rewind one, since the loop will increment it again + n--; + + // Hexadecimal escape sequences produce exact value, even if it is not proper unicode chars + if( engine->ep.stringEncoding == 0 ) + { + str.PushLast((asBYTE)val); + } + else + { +#ifndef AS_BIG_ENDIAN + str.PushLast((asBYTE)val); + str.PushLast((asBYTE)(val>>8)); +#else + str.PushLast((asBYTE)(val>>8)); + str.PushLast((asBYTE)val); +#endif + } + if( charLiteral == -1 ) charLiteral = val; + continue; + } + else if( cstr[n] == 'u' || cstr[n] == 'U' ) + { + // \u expects 4 hex digits + // \U expects 8 hex digits + bool expect2 = cstr[n] == 'u'; + int c = expect2 ? 4 : 8; + + val = 0; + + for( ; c > 0; c-- ) + { + ++n; + if( n == cstr.GetLength() ) break; + + if( cstr[n] >= '0' && cstr[n] <= '9' ) + val = val*16 + cstr[n] - '0'; + else if( cstr[n] >= 'a' && cstr[n] <= 'f' ) + val = val*16 + cstr[n] - 'a' + 10; + else if( cstr[n] >= 'A' && cstr[n] <= 'F' ) + val = val*16 + cstr[n] - 'A' + 10; + else + break; + } + + if( c != 0 ) + { + // Give warning about invalid code point + // TODO: Need code position for warning + asCString msg; + msg.Format(TXT_INVALID_UNICODE_FORMAT_EXPECTED_d, expect2 ? 4 : 8); + Warning(msg, node); + continue; + } + } + else + { + if( cstr[n] == '"' ) + val = '"'; + else if( cstr[n] == '\'' ) + val = '\''; + else if( cstr[n] == 'n' ) + val = '\n'; + else if( cstr[n] == 'r' ) + val = '\r'; + else if( cstr[n] == 't' ) + val = '\t'; + else if( cstr[n] == '0' ) + val = '\0'; + else if( cstr[n] == '\\' ) + val = '\\'; + else + { + // Invalid escape sequence + Warning(TXT_INVALID_ESCAPE_SEQUENCE, node); + continue; + } + } + } + else + { + if( engine->ep.scanner == 1 && (cstr[n] & 0x80) ) + { + unsigned int len; + val = asStringDecodeUTF8(&cstr[n], &len); + if( val == 0xFFFFFFFF ) + { + // Incorrect UTF8 encoding. Use only the first byte + // TODO: Need code position for warning + Warning(TXT_INVALID_UNICODE_SEQUENCE_IN_SRC, node); + val = (unsigned char)cstr[n]; + } + else + n += len-1; + } + else + val = (unsigned char)cstr[n]; + } + + // Add the character to the final string + char encodedValue[5]; + int len; + if( engine->ep.scanner == 1 && engine->ep.stringEncoding == 0 ) + { + // Convert to UTF8 encoded + len = asStringEncodeUTF8(val, encodedValue); + } + else if( engine->ep.stringEncoding == 1 ) + { + // Convert to 16bit wide character string (even if the script is scanned as ASCII) + len = asStringEncodeUTF16(val, encodedValue); + } + else + { + // Do not convert ASCII characters + encodedValue[0] = (asBYTE)val; + len = 1; + } + + if( len < 0 ) + { + // Give warning about invalid code point + // TODO: Need code position for warning + Warning(TXT_INVALID_UNICODE_VALUE, node); + } + else + { + // Add the encoded value to the final string + str.Concatenate(encodedValue, len); + if( charLiteral == -1 ) charLiteral = val; + } + } + + cstr.Assign(str.AddressOf(), str.GetLength()); + return charLiteral; +} + +void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *node) +{ + // Remove first line if it only contains whitespace + bool isMultiline = false; + int start; + for( start = 0; start < (int)str.GetLength(); start++ ) + { + if( str[start] == '\n' ) + { + isMultiline = true; + + // Remove the linebreak as well + start++; + break; + } + + if( str[start] != ' ' && + str[start] != '\t' && + str[start] != '\r' ) + { + // Don't remove anything + start = 0; + break; + } + } + + // Remove the line after the last line break if it only contains whitespaces + int end; + for( end = (int)str.GetLength() - 1; end >= 0; end-- ) + { + if( str[end] == '\n' ) + { + // Don't remove the last line break + end++; + break; + } + + if( str[end] != ' ' && + str[end] != '\t' && + str[end] != '\r' ) + { + // Don't remove anything + end = (int)str.GetLength(); + break; + } + } + + if( end < 0 ) end = 0; + + asCString tmp; + if (end > start || engine->ep.heredocTrimMode != 2 ) + { + // if heredocTrimMode == 0 the string shouldn't be trimmed + // if heredocTrimMode == 1 the string should only be trimmed if it is multiline + // if heredocTrimMode == 2 the string should always be trimmed + if (engine->ep.heredocTrimMode == 2 || (isMultiline && engine->ep.heredocTrimMode == 1)) + tmp.Assign(&str[start], end - start); + else + tmp = str; + } + + ProcessStringConstant(tmp, node, false); + + str = tmp; +} + +int asCCompiler::CompileConversion(asCScriptNode *node, asCExprContext *ctx) +{ + asCExprContext expr(engine); + asCDataType to; + bool anyErrors = false; + EImplicitConv convType; + if( node->nodeType == snConstructCall ) + { + convType = asIC_EXPLICIT_VAL_CAST; + + // Verify that there is only one argument + if( node->lastChild->firstChild == 0 || + node->lastChild->firstChild != node->lastChild->lastChild ) + { + Error(TXT_ONLY_ONE_ARGUMENT_IN_CAST, node->lastChild); + expr.type.SetDummy(); + anyErrors = true; + } + else + { + // Compile the expression + int r = CompileAssignment(node->lastChild->firstChild, &expr); + if( r < 0 ) + anyErrors = true; + } + + // Determine the requested type + to = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace); + to.MakeReadOnly(true); // Default to const + asASSERT(to.IsPrimitive()); + } + else + { + convType = asIC_EXPLICIT_REF_CAST; + + // Compile the expression + int r = CompileAssignment(node->lastChild, &expr); + if( r < 0 ) + anyErrors = true; + + // Determine the requested type + to = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace); + + // If the type support object handles, then use it + if( to.SupportHandles() ) + { + to.MakeHandle(true); + if( expr.type.dataType.IsObjectConst() ) + to.MakeHandleToConst(true); + } + else if( !to.IsObjectHandle() ) + { + // The cast operator can only be used for reference casts + Error(TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST, node->firstChild); + anyErrors = true; + } + } + + // Do not allow casting to non shared type if we're compiling a shared method + if( outFunc->IsShared() && + to.GetTypeInfo() && !to.GetTypeInfo()->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, to.GetTypeInfo()->name.AddressOf()); + Error(msg, node); + anyErrors = true; + } + + if( anyErrors ) + { + // Assume that the error can be fixed and allow the compilation to continue + ctx->type.Set(to); + return -1; + } + + ProcessPropertyGetAccessor(&expr, node); + + // Don't allow any operators on expressions that take address of class method + if( expr.IsClassMethod() ) + { + Error(TXT_INVALID_OP_ON_METHOD, node); + return -1; + } + + // We don't want a reference for conversion casts + if( convType == asIC_EXPLICIT_VAL_CAST && expr.type.dataType.IsReference() ) + { + if( expr.type.dataType.IsObject() ) + Dereference(&expr, true); + else + ConvertToVariable(&expr); + } + + ImplicitConversion(&expr, to, node, convType); + + IsVariableInitialized(&expr.type, node); + + // If no type conversion is really tried ignore it + if( to == expr.type.dataType ) + { + // This will keep information about constant type + MergeExprBytecode(ctx, &expr); + ctx->type = expr.type; + return 0; + } + + if( to.IsEqualExceptRefAndConst(expr.type.dataType) && to.IsPrimitive() ) + { + MergeExprBytecode(ctx, &expr); + ctx->type = expr.type; + ctx->type.dataType.MakeReadOnly(true); + return 0; + } + + // The implicit conversion already does most of the conversions permitted, + // here we'll only treat those conversions that require an explicit cast. + + bool conversionOK = false; + if( !expr.type.isConstant && expr.type.dataType != asCDataType::CreatePrimitive(ttVoid, false) ) + { + if( !expr.type.dataType.IsObject() ) + ConvertToTempVariable(&expr); + + if( to.IsObjectHandle() && + expr.type.dataType.IsObjectHandle() && + !(!to.IsHandleToConst() && expr.type.dataType.IsHandleToConst()) ) + { + conversionOK = CompileRefCast(&expr, to, true, node); + + MergeExprBytecode(ctx, &expr); + ctx->type = expr.type; + } + } + + if( conversionOK ) + return 0; + + // Conversion not available + ctx->type.SetDummy(); + + asCString strTo, strFrom; + + strTo = to.Format(outFunc->nameSpace); + strFrom = expr.type.dataType.Format(outFunc->nameSpace); + + asCString msg; + msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf()); + + Error(msg, node); + return -1; +} + +void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, asCExprContext *ctx, bool deferAll) +{ + // deferAll is set to true if for example the function returns a reference, since in + // this case the function might be returning a reference to one of the arguments. + + asCScriptFunction *descr = builder->GetFunctionDescription(funcID); + + // Parameters that are sent by reference should be assigned + // to the evaluated expression if it is an lvalue + + // Evaluate the arguments from last to first + int n = (int)descr->parameterTypes.GetLength() - 1; + for( ; n >= 0; n-- ) + { + // All &out arguments must be deferred, except if the argument is clean, in which case the actual reference was passed in to the function + // If deferAll is set all objects passed by reference or handle must be deferred + if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF) && !args[n]->isCleanArg) || + (descr->parameterTypes[n].IsObject() && deferAll && (descr->parameterTypes[n].IsReference() || descr->parameterTypes[n].IsObjectHandle())) ) + { + asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF) && !args[n]->isCleanArg) || args[n]->origExpr ); + + // For &inout, only store the argument if it is for a temporary variable + if( engine->ep.allowUnsafeReferences || + descr->inOutFlags[n] != asTM_INOUTREF || args[n]->type.isTemporary ) + { + // Store the argument for later processing + asSDeferredParam outParam; + outParam.argNode = args[n]->exprNode; + outParam.argType = args[n]->type; + outParam.argInOutFlags = descr->inOutFlags[n]; + outParam.origExpr = args[n]->origExpr; + + ctx->deferredParams.PushLast(outParam); + } + } + else + { + // Release the temporary variable now + ReleaseTemporaryVariable(args[n]->type, &ctx->bc); + } + + // Move the argument's deferred expressions over to the final expression + for( asUINT m = 0; m < args[n]->deferredParams.GetLength(); m++ ) + { + ctx->deferredParams.PushLast(args[n]->deferredParams[m]); + args[n]->deferredParams[m].origExpr = 0; + } + args[n]->deferredParams.SetLength(0); + } +} + +void asCCompiler::ProcessDeferredParams(asCExprContext *ctx) +{ + if( isProcessingDeferredParams ) return; + + isProcessingDeferredParams = true; + + for( asUINT n = 0; n < ctx->deferredParams.GetLength(); n++ ) + { + asSDeferredParam outParam = ctx->deferredParams[n]; + if( outParam.argInOutFlags < asTM_OUTREF ) // &in, or not reference + { + // Just release the variable + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + else if( outParam.argInOutFlags == asTM_OUTREF ) + { + asCExprContext *expr = outParam.origExpr; + outParam.origExpr = 0; + + if( outParam.argType.dataType.IsObjectHandle() ) + { + // Implicitly convert the value to a handle + if( expr->type.dataType.IsObjectHandle() ) + expr->type.isExplicitHandle = true; + } + + // Verify that the expression result in a lvalue, or a property accessor + if( IsLValue(expr->type) || expr->property_get || expr->property_set ) + { + asCExprContext rctx(engine); + rctx.type = outParam.argType; + if( rctx.type.dataType.IsPrimitive() ) + rctx.type.dataType.MakeReference(false); + else + { + rctx.bc.InstrSHORT(asBC_PSF, outParam.argType.stackOffset); + rctx.type.dataType.MakeReference(IsVariableOnHeap(outParam.argType.stackOffset)); + if( expr->type.isExplicitHandle ) + rctx.type.isExplicitHandle = true; + } + + asCExprContext o(engine); + DoAssignment(&o, expr, &rctx, outParam.argNode, outParam.argNode, ttAssignment, outParam.argNode); + + if( !o.type.dataType.IsPrimitive() ) o.bc.Instr(asBC_PopPtr); + + // The assignment may itself have resulted in a new temporary variable, e.g. if + // the opAssign returns a non-reference. We must release this temporary variable + // since it won't be used + ReleaseTemporaryVariable(o.type, &o.bc); + + MergeExprBytecode(ctx, &o); + } + else + { + // We must still evaluate the expression + MergeExprBytecode(ctx, expr); + if( !expr->IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) + ctx->bc.Instr(asBC_PopPtr); + + // Give an error, except if the argument is void, null or 0 which indicate the argument is explicitly to be ignored + if( !expr->IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.GetConstantData() == 0) ) + Error(TXT_ARG_NOT_LVALUE, outParam.argNode); + + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + + ReleaseTemporaryVariable(expr->type, &ctx->bc); + + // Delete the original expression context + asDELETE(expr, asCExprContext); + } + else // &inout + { + if( outParam.argType.isTemporary ) + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + else if( !outParam.argType.isVariable ) + { + if( outParam.argType.dataType.IsObject() && + ((outParam.argType.dataType.GetBehaviour()->addref && + outParam.argType.dataType.GetBehaviour()->release) || + (outParam.argType.dataType.GetTypeInfo()->flags & asOBJ_NOCOUNT)) ) + { + // Release the object handle that was taken to guarantee the reference + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + } + } + } + + ctx->deferredParams.SetLength(0); + isProcessingDeferredParams = false; +} + + +int asCCompiler::CompileConstructCall(asCScriptNode *node, asCExprContext *ctx) +{ + // The first node is a datatype node + asCString name; + asCExprValue tempObj; + bool onHeap = true; + asCArray funcs; + bool error = false; + + // It is possible that the name is really a constructor + asCDataType dt; + dt = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace); + if( dt.IsPrimitive() ) + { + // This is a cast to a primitive type + return CompileConversion(node, ctx); + } + + if( dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + // Types declared as implicit handle must not attempt to construct a handle + dt.MakeHandle(false); + } + + // Don't accept syntax like object@(expr) + if( dt.IsObjectHandle() ) + { + asCString str; + str.Format(TXT_CANT_CONSTRUCT_s_USE_REF_CAST, dt.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return -1; + } + + // Make sure the desired type can actually be instantiated + // Delegates are allowed to be created through construct calls, + // even though they cannot be instantiated as variables + if( !dt.CanBeInstantiated() && !dt.IsFuncdef() ) + { + asCString str; + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return -1; + } + + // Do not allow constructing non-shared types in shared functions + if( outFunc->IsShared() && + dt.GetTypeInfo() && !dt.GetTypeInfo()->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetTypeInfo()->name.AddressOf()); + Error(msg, node); + return -1; + } + + // Compile the arguments + asCArray args; + asCArray namedArgs; + asCArray temporaryVariables; + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) + { + // Check for a value cast behaviour + if( args.GetLength() == 1 && args[0]->type.dataType.GetTypeInfo() ) + { + asCExprContext conv(engine); + conv.type = args[0]->type; + asUINT cost = ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false); + + // Don't use this if the cost is 0 because it would mean that nothing + // is done and the scipt wants a new value to be constructed + if( conv.type.dataType.IsEqualExceptRef(dt) && cost > 0 ) + { + // Make sure any property accessor is already evaluated + ProcessPropertyGetAccessor(args[0], args[0]->exprNode); + + ImplicitConversion(args[0], dt, node->lastChild, asIC_EXPLICIT_VAL_CAST); + + ctx->bc.AddCode(&args[0]->bc); + ctx->type = args[0]->type; + + asDELETE(args[0], asCExprContext); + + return 0; + } + } + + // Check for possible constructor/factory + name = dt.Format(outFunc->nameSpace); + + asSTypeBehaviour *beh = dt.GetBehaviour(); + + if( !(dt.GetTypeInfo()->flags & asOBJ_REF) && !dt.IsFuncdef() ) + { + funcs = beh->constructors; + + // Value types and script types are allocated through the constructor + tempObj.dataType = dt; + tempObj.stackOffset = (short)AllocateVariable(dt, true); + tempObj.dataType.MakeReference(true); + tempObj.isTemporary = true; + tempObj.isVariable = true; + + onHeap = IsVariableOnHeap(tempObj.stackOffset); + + // Push the address of the object on the stack + if( onHeap ) + ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset); + } + else if( beh ) + funcs = beh->factories; + + // Special case: Allow calling func(void) with a void expression. + if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) ) + { + // Evaluate the expression before the function call + MergeExprBytecode(ctx, args[0]); + asDELETE(args[0], asCExprContext); + args.SetLength(0); + } + + // Special case: If this is an object constructor and there are no arguments use the default constructor. + // If none has been registered, just allocate the variable and push it on the stack. + if( args.GetLength() == 0 ) + { + beh = tempObj.dataType.GetBehaviour(); + if( beh && beh->construct == 0 && !(dt.GetTypeInfo()->flags & asOBJ_REF) ) + { + // Call the default constructor + ctx->type = tempObj; + + if( onHeap ) + { + asASSERT(ctx->bc.GetLastInstr() == asBC_VAR); + ctx->bc.RemoveLastInstr(); + } + + CallDefaultConstructor(tempObj.dataType, tempObj.stackOffset, IsVariableOnHeap(tempObj.stackOffset), &ctx->bc, node); + + // Push the reference on the stack + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + return 0; + } + } + + // Special case: If this is a construction of a delegate and the expression names an object method + if( dt.IsFuncdef() && args.GetLength() == 1 && args[0]->methodName != "" ) + { + // TODO: delegate: It is possible that the argument returns a function pointer already, in which + // case no object delegate will be created, but instead a delegate for a function pointer + // In theory a simple cast would be good in this case, but this is a construct call so it + // is expected that a new object is created. + + dt.MakeHandle(true); + ctx->type.Set(dt); + + // The delegate must be able to hold on to a reference to the object + if( !args[0]->type.dataType.SupportHandles() ) + { + Error(TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES, node); + error = true; + } + else + { + // Filter the available object methods to find the one that matches the func def + asCObjectType *type = CastToObjectType(args[0]->type.dataType.GetTypeInfo()); + asCScriptFunction *bestMethod = 0; + for( asUINT n = 0; n < type->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[type->methods[n]]; + + if( func->name != args[0]->methodName ) + continue; + + // If the expression is for a const object, then only const methods should be accepted + if( args[0]->type.dataType.IsReadOnly() && !func->IsReadOnly() ) + continue; + + if( func->IsSignatureExceptNameAndObjectTypeEqual(CastToFuncdefType(dt.GetTypeInfo())->funcdef) ) + { + bestMethod = func; + + // If the expression is non-const the non-const overloaded method has priority + if( args[0]->type.dataType.IsReadOnly() == func->IsReadOnly() ) + break; + } + } + + if( bestMethod ) + { + // The object pointer is already on the stack + MergeExprBytecode(ctx, args[0]); + + // Push the function pointer as an additional argument + ctx->bc.InstrPTR(asBC_FuncPtr, bestMethod); + + // Call the factory function for the delegate + asCArray delegateFuncs; + builder->GetFunctionDescriptions(DELEGATE_FACTORY, delegateFuncs, engine->nameSpaces[0]); + asASSERT(delegateFuncs.GetLength() == 1 ); + ctx->bc.Call(asBC_CALLSYS , delegateFuncs[0], 2*AS_PTR_SIZE); + + // Store the returned delegate in a temporary variable + int returnOffset = AllocateVariable(dt, true, false); + dt.MakeReference(true); + ctx->type.SetVariable(dt, returnOffset, true); + ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); + + // Push a reference to the temporary variable on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + + // Clean up arguments + ReleaseTemporaryVariable(args[0]->type, &ctx->bc); + } + else + { + asCString msg; + msg.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, CastToFuncdefType(dt.GetTypeInfo())->funcdef->GetDeclaration()); + Error(msg.AddressOf(), node); + error = true; + } + } + + // Clean-up arg + asDELETE(args[0], asCExprContext); + return error ? -1 : 0; + } + + MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, 0, false); + + if( funcs.GetLength() != 1 ) + { + // The error was reported by MatchFunctions() + error = true; + + // Dummy value + ctx->type.SetDummy(); + } + else + { + // TODO: Clean up: Merge this with MakeFunctionCall + + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(dt.GetTypeInfo()), &namedArgs); + + if( r == asSUCCESS ) + { + asCByteCode objBC(engine); + + PrepareFunctionCall(funcs[0], &ctx->bc, args); + + MoveArgsToStack(funcs[0], &ctx->bc, args, false); + + if( !(dt.GetTypeInfo()->flags & asOBJ_REF) ) + { + // If the object is allocated on the stack, then call the constructor as a normal function + if( onHeap ) + { + int offset = 0; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]); + for( asUINT n = 0; n < args.GetLength(); n++ ) + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + + ctx->bc.InstrWORD(asBC_GETREF, (asWORD)offset); + } + else + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + + PerformFunctionCall(funcs[0], ctx, onHeap, &args, CastToObjectType(tempObj.dataType.GetTypeInfo())); + + // Add tag that the object has been initialized + ctx->bc.ObjInfo(tempObj.stackOffset, asOBJ_INIT); + + // The constructor doesn't return anything, + // so we have to manually inform the type of + // the return value + ctx->type = tempObj; + if( !onHeap ) + ctx->type.dataType.MakeReference(false); + + // Push the address of the object on the stack again + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + } + else + { + // Call the factory to create the reference type + PerformFunctionCall(funcs[0], ctx, false, &args); + } + } + else + error = true; + } + } + else + { + // Failed to compile the argument list, set the result to the dummy type + ctx->type.SetDummy(); + error = true; + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx, asCExprContext); + } + + return error ? -1 : 0; +} + + +int asCCompiler::CompileFunctionCall(asCScriptNode *node, asCExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope) +{ + asCExprValue tempObj; + asCArray funcs; + int localVar = -1; + bool initializeMembers = false; + asCExprContext funcExpr(engine); + + asCScriptNode *nm = node->lastChild->prev; + asCString name(&script->code[nm->tokenPos], nm->tokenLength); + + // First check for a local variable as it would take precedence + // Must not allow function names, nor global variables to be returned in this instance + // If objectType is set then this is a post op expression and we shouldn't look for local variables + if( objectType == 0 ) + { + localVar = CompileVariableAccess(name, scope, &funcExpr, node, true, true, true); + if( localVar >= 0 && + !(funcExpr.type.dataType.IsFuncdef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) + { + // The variable is not a function or object with opCall + asCString msg; + msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf()); + Error(msg, node); + return -1; + } + + // If the name matches a method name, then reset the indicator that nothing was found + if( funcExpr.methodName != "" ) + localVar = -1; + } + + if( localVar < 0 ) + { + // If this is an expression post op, or if a class method is + // being compiled, then we should look for matching class methods + if( objectType || (outFunc && outFunc->objectType && scope != "::") ) + { + // If we're compiling a constructor and the name of the function is super then + // the constructor of the base class is being called. + // super cannot be prefixed with a scope operator + if( scope == "" && m_isConstructor && name == SUPER_TOKEN ) + { + // If the class is not derived from anyone else, calling super should give an error + if( outFunc && outFunc->objectType->derivedFrom ) + funcs = outFunc->objectType->derivedFrom->beh.constructors; + + // Must not allow calling base class' constructor multiple times + if( continueLabels.GetLength() > 0 ) + { + // If a continue label is set we are in a loop + Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node); + } + else if( breakLabels.GetLength() > 0 ) + { + // TODO: inheritance: Should eventually allow constructors in switch statements + // If a break label is set we are either in a loop or a switch statements + Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node); + } + else if( m_isConstructorCalled ) + { + Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node); + } + m_isConstructorCalled = true; + + // We need to initialize the class members, but only after all the deferred arguments have been completed + initializeMembers = true; + } + else + { + // The scope can be used to specify the base class + builder->GetObjectMethodDescriptions(name.AddressOf(), objectType ? objectType : outFunc->objectType, funcs, objIsConst, scope, node, script); + } + + // It is still possible that there is a class member of a function type or a type with opCall methods + if( funcs.GetLength() == 0 ) + { + int r = CompileVariableAccess(name, scope, &funcExpr, node, true, true, true, objectType); + if( r >= 0 && + !(funcExpr.type.dataType.IsFuncdef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) + { + // The variable is not a function + asCString msg; + msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf()); + Error(msg, node); + return -1; + } + + // If the name is an access property, make sure the original value isn't + // dereferenced when calling the access property as part a dot post operator + if( objectType && (funcExpr.property_get || funcExpr.property_set) && !ctx->type.dataType.IsReference() ) + funcExpr.property_ref = false; + } + + // If a class method is being called implicitly, then add the this pointer for the call + if( funcs.GetLength() && !objectType ) + { + objectType = outFunc->objectType; + + asCDataType dt = asCDataType::CreateType(objectType, false); + + // The object pointer is located at stack position 0 + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + + Dereference(ctx, true); + } + } + + // If it is not a class method or member function pointer, + // then look for global functions or global function pointers, + // unless this is an expression post op, incase only member + // functions are expected + if( objectType == 0 && funcs.GetLength() == 0 && (!funcExpr.type.dataType.IsFuncdef() || funcExpr.type.dataType.IsObject()) ) + { + // The scope is used to define the namespace + asSNameSpace *ns = DetermineNameSpace(scope); + if( ns ) + { + // Search recursively in parent namespaces + while( ns && funcs.GetLength() == 0 && !funcExpr.type.dataType.IsFuncdef() ) + { + builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); + if( funcs.GetLength() == 0 ) + { + int r = CompileVariableAccess(name, scope, &funcExpr, node, true, true); + if( r >= 0 && + !(funcExpr.type.dataType.IsFuncdef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) + { + // The variable is not a function + asCString msg; + msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf()); + Error(msg, node); + return -1; + } + } + + ns = engine->GetParentNameSpace(ns); + } + } + else + { + asCString msg; + msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf()); + Error(msg, node); + return -1; + } + } + } + + if( funcs.GetLength() == 0 ) + { + if( funcExpr.type.dataType.IsFuncdef() ) + { + funcs.PushLast(CastToFuncdefType(funcExpr.type.dataType.GetTypeInfo())->funcdef->id); + } + else if( funcExpr.type.dataType.IsObject() ) + { + // Keep information about temporary variables as deferred expression so it can be properly cleaned up after the call + if( ctx->type.isTemporary ) + { + asASSERT( objectType ); + + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, ctx->type.stackOffset, true); + + ctx->deferredParams.PushLast(deferred); + } + if( funcExpr.property_get == 0 ) + Dereference(ctx, true); + + // Add the bytecode for accessing the object on which opCall will be called + MergeExprBytecodeAndType(ctx, &funcExpr); + ProcessPropertyGetAccessor(ctx, node); + Dereference(ctx, true); + + objectType = CastToObjectType(funcExpr.type.dataType.GetTypeInfo()); + + // Get the opCall methods from the object type + if( funcExpr.type.dataType.IsObjectHandle() ) + objIsConst = funcExpr.type.dataType.IsHandleToConst(); + else + objIsConst = funcExpr.type.dataType.IsReadOnly(); + + builder->GetObjectMethodDescriptions("opCall", CastToObjectType(funcExpr.type.dataType.GetTypeInfo()), funcs, objIsConst); + } + } + + // Compile the arguments + asCArray args; + asCArray namedArgs; + + bool isOK = true; + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) + { + // Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void' + if( args.GetLength() == 1 && args[0]->type.IsVoid() && !args[0]->IsVoidExpression() ) + { + // Evaluate the expression before the function call + MergeExprBytecode(ctx, args[0]); + asDELETE(args[0], asCExprContext); + args.SetLength(0); + } + + MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, objectType, objIsConst, false, true, scope); + + if( funcs.GetLength() != 1 ) + { + // The error was reported by MatchFunctions() + + // Dummy value + ctx->type.SetDummy(); + isOK = false; + } + else + { + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType, &namedArgs); + + // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or + // is it enough to make sure it is in a local variable? + + // For function pointer we must guarantee that the function is safe, i.e. + // by first storing the function pointer in a local variable (if it isn't already in one) + if( r == asSUCCESS ) + { + asCScriptFunction *func = builder->GetFunctionDescription(funcs[0]); + if( func->funcType == asFUNC_FUNCDEF ) + { + if( objectType && funcExpr.property_get <= 0 ) + { + // Dereference the object pointer to access the member + Dereference(ctx, true); + } + + if( funcExpr.property_get > 0 ) + { + ProcessPropertyGetAccessor(&funcExpr, node); + Dereference(&funcExpr, true); + } + else + { + Dereference(&funcExpr, true); + ConvertToVariable(&funcExpr); + } + + // The actual function should be called as if a global function + objectType = 0; + + // The function call will be made directly from the local variable so the function pointer shouldn't be on the stack + funcExpr.bc.Instr(asBC_PopPtr); + + asCExprValue tmp = ctx->type; + MergeExprBytecodeAndType(ctx, &funcExpr); + ReleaseTemporaryVariable(tmp, &ctx->bc); + } + + MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, funcExpr.type.stackOffset); + } + else + isOK = false; + } + } + else + { + // Failed to compile the argument list, set the dummy type and continue compilation + ctx->type.SetDummy(); + isOK = false; + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx, asCExprContext); + } + + if( initializeMembers ) + { + asASSERT( m_isConstructor ); + + // Need to initialize members here, as they may use the properties of the base class + // If there are multiple paths that call super(), then there will also be multiple + // locations with initializations of the members. It is not possible to consolidate + // these in one place, as the expressions for the initialization are evaluated where + // they are compiled, which means that they may access different variables depending + // on the scope where super() is called. + // Members that don't have an explicit initialization expression will be initialized + // beginning of the constructor as they are guaranteed not to use at the any + // members of the base class. + CompileMemberInitialization(&ctx->bc, false); + } + + return isOK ? 0 : -1; +} + +asSNameSpace *asCCompiler::DetermineNameSpace(const asCString &scope) +{ + asSNameSpace *ns; + + if( scope == "" ) + { + // When compiling default argument expression the correct namespace is stored in the outFunc even for objects + if( outFunc->nameSpace->name != "" || isCompilingDefaultArg ) + ns = outFunc->nameSpace; + else if( outFunc->objectType && outFunc->objectType->nameSpace->name != "" ) + ns = outFunc->objectType->nameSpace; + else + ns = engine->nameSpaces[0]; + } + else if( scope == "::" ) + ns = engine->nameSpaces[0]; + else + ns = engine->FindNameSpace(scope.AddressOf()); + + return ns; +} + +int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asCExprContext *ctx) +{ + int op = node->tokenType; + + // Don't allow any prefix operators except handle on expressions that take address of class method + if( ctx->IsClassMethod() && op != ttHandle ) + { + Error(TXT_INVALID_OP_ON_METHOD, node); + return -1; + } + + // Don't allow any operators on void expressions + if( ctx->IsVoidExpression() ) + { + Error(TXT_VOID_CANT_BE_OPERAND, node); + return -1; + } + + IsVariableInitialized(&ctx->type, node); + + if( op == ttHandle ) + { + if( ctx->methodName != "" ) + { + // Don't allow taking the handle of a handle + if( ctx->type.isExplicitHandle ) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node); + return -1; + } + } + else + { + // Don't allow taking handle of a handle, i.e. @@ + if( ctx->type.isExplicitHandle ) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node); + return -1; + } + + // @null is allowed even though it is implicit + if( !ctx->type.IsNullConstant() ) + { + // Verify that the type allow its handle to be taken + if( !ctx->type.dataType.SupportHandles() && !ctx->type.dataType.IsObjectHandle() ) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node); + return -1; + } + + // Objects that are not local variables are not references + // Objects allocated on the stack are also not marked as references + if( !ctx->type.dataType.IsReference() && + !((ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && !ctx->type.isVariable) && + !(ctx->type.isVariable && !IsVariableOnHeap(ctx->type.stackOffset)) ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + // Convert the expression to a handle + if( !ctx->type.dataType.IsObjectHandle() && !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) ) + { + asCDataType to = ctx->type.dataType; + to.MakeHandle(true); + to.MakeReference(true); + to.MakeHandleToConst(ctx->type.dataType.IsReadOnly()); + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV, true, false); + + asASSERT( ctx->type.dataType.IsObjectHandle() ); + } + else if( ctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE ) + { + // For the ASHANDLE type we'll simply set the expression as a handle + ctx->type.dataType.MakeHandle(true); + } + } + } + + // Mark the expression as an explicit handle to avoid implicit conversions to non-handle expressions + ctx->type.isExplicitHandle = true; + } + else if( (op == ttMinus || op == ttPlus || op == ttBitNot || op == ttInc || op == ttDec) && ctx->type.dataType.IsObject() ) + { + // Look for the appropriate method + // There is no overloadable operator for unary plus + const char *opName = 0; + switch( op ) + { + case ttMinus: opName = "opNeg"; break; + case ttBitNot: opName = "opCom"; break; + case ttInc: opName = "opPreInc"; break; + case ttDec: opName = "opPreDec"; break; + } + + if( opName ) + { + // TODO: Should convert this to something similar to CompileOverloadedDualOperator2 + ProcessPropertyGetAccessor(ctx, node); + + // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method + + // Find the correct method + bool isConst = ctx->type.dataType.IsObjectConst(); + asCArray funcs; + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == opName && + func->parameterTypes.GetLength() == 0 && + (!isConst || func->isReadOnly) ) + { + funcs.PushLast(func->id); + } + } + + // Did we find the method? + if( funcs.GetLength() == 1 ) + { + asCArray args; + MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + return 0; + } + else if( funcs.GetLength() == 0 ) + { + asCString str; + str = asCString(opName) + "()"; + if( isConst ) + str += " const"; + str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return -1; + } + else if( funcs.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(funcs, node); + + ctx->type.SetDummy(); + return -1; + } + } + else if( op == ttPlus ) + { + Error(TXT_ILLEGAL_OPERATION, node); + ctx->type.SetDummy(); + return -1; + } + } + else if( op == ttPlus || op == ttMinus ) + { + // This is only for primitives. Objects are treated in the above block + + // Make sure the type is a math type + if( !(ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsFloatType() || + ctx->type.dataType.IsDoubleType() ) ) + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + + ProcessPropertyGetAccessor(ctx, node); + + asCDataType to = ctx->type.dataType; + + if( ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + to = asCDataType::CreatePrimitive(ttInt8, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + to = asCDataType::CreatePrimitive(ttInt16, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + to = asCDataType::CreatePrimitive(ttInt, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 ) + to = asCDataType::CreatePrimitive(ttInt64, false); + else + { + Error(TXT_INVALID_TYPE, node); + return -1; + } + } + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + + // Use an explicit conversion in case of constants to avoid unnecessary warning about change of sign + ImplicitConversion(ctx, to, node, ctx->type.isConstant ? asIC_EXPLICIT_VAL_CAST : asIC_IMPLICIT_CONV); + + if( !ctx->type.isConstant ) + { + ConvertToTempVariable(ctx); + asASSERT(!ctx->type.isLValue); + + if( op == ttMinus ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_NEGi, ctx->type.stackOffset); + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + ctx->bc.InstrSHORT(asBC_NEGi64, ctx->type.stackOffset); + else if( ctx->type.dataType.IsFloatType() ) + ctx->bc.InstrSHORT(asBC_NEGf, ctx->type.stackOffset); + else if( ctx->type.dataType.IsDoubleType() ) + ctx->bc.InstrSHORT(asBC_NEGd, ctx->type.stackOffset); + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + return 0; + } + } + else + { + if( op == ttMinus ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->type.SetConstantDW(-(int)ctx->type.GetConstantDW()); + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + ctx->type.SetConstantQW(-(asINT64)ctx->type.GetConstantQW()); + else if( ctx->type.dataType.IsFloatType() ) + ctx->type.SetConstantF(-ctx->type.GetConstantF()); + else if( ctx->type.dataType.IsDoubleType() ) + ctx->type.SetConstantD(-ctx->type.GetConstantD()); + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + return 0; + } + } + } + else if( op == ttNot ) + { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( ctx->type.dataType.GetTypeInfo() && (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_IMPLICIT_CONV); + + if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + { + if( ctx->type.isConstant ) + { + #if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(ctx->type.GetConstantB() == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + #else + ctx->type.SetConstantDW(ctx->type.GetConstantDW() == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + #endif + return 0; + } + + ProcessPropertyGetAccessor(ctx, node); + + ConvertToTempVariable(ctx); + asASSERT(!ctx->type.isLValue); + + ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else if( op == ttBitNot ) + { + ProcessPropertyGetAccessor(ctx, node); + + asCDataType to = ctx->type.dataType; + + if( ctx->type.dataType.IsIntegerType() ) + { + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + to = asCDataType::CreatePrimitive(ttUInt8, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + to = asCDataType::CreatePrimitive(ttUInt16, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + to = asCDataType::CreatePrimitive(ttUInt, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 ) + to = asCDataType::CreatePrimitive(ttUInt64, false); + else + { + Error(TXT_INVALID_TYPE, node); + return -1; + } + } + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + if( ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.isConstant ) + { + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + ctx->type.SetConstantB(~ctx->type.GetConstantB()); + else if (ctx->type.dataType.GetSizeInMemoryBytes() == 2) + ctx->type.SetConstantW(~ctx->type.GetConstantW()); + else if (ctx->type.dataType.GetSizeInMemoryBytes() == 4) + ctx->type.SetConstantDW(~ctx->type.GetConstantDW()); + else + ctx->type.SetConstantQW(~ctx->type.GetConstantQW()); + return 0; + } + + ConvertToTempVariable(ctx); + asASSERT(!ctx->type.isLValue); + + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_BNOT, ctx->type.stackOffset); + else + ctx->bc.InstrSHORT(asBC_BNOT64, ctx->type.stackOffset); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else if( op == ttInc || op == ttDec ) + { + // Need a reference to the primitive that will be updated + // The result of this expression is the same reference as before + + // Make sure the reference isn't a temporary variable + if( ctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, node); + return -1; + } + if( ctx->type.dataType.IsReadOnly() ) + { + Error(TXT_REF_IS_READ_ONLY, node); + return -1; + } + if( ctx->property_get || ctx->property_set ) + { + Error(TXT_INVALID_REF_PROP_ACCESS, node); + return -1; + } + if( !ctx->type.isLValue ) + { + Error(TXT_NOT_LVALUE, node); + return -1; + } + + if( ctx->type.isVariable && !ctx->type.dataType.IsReference() ) + ConvertToReference(ctx); + else if( !ctx->type.dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi64); + else + ctx->bc.Instr(asBC_DECi64); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi); + else + ctx->bc.Instr(asBC_DECi); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi16); + else + ctx->bc.Instr(asBC_DECi16); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi8); + else + ctx->bc.Instr(asBC_DECi8); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttFloat, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCf); + else + ctx->bc.Instr(asBC_DECf); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttDouble, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCd); + else + ctx->bc.Instr(asBC_DECd); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else + { + // Unknown operator + asASSERT(false); + return -1; + } + + return 0; +} + +void asCCompiler::ConvertToReference(asCExprContext *ctx) +{ + if( ctx->type.isVariable && !ctx->type.dataType.IsReference() ) + { + ctx->bc.InstrSHORT(asBC_LDV, ctx->type.stackOffset); + ctx->type.dataType.MakeReference(true); + ctx->type.SetVariable(ctx->type.dataType, ctx->type.stackOffset, ctx->type.isTemporary); + } +} + +int asCCompiler::FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess) +{ + return FindPropertyAccessor(name, ctx, 0, node, ns, isThisAccess); +} + +int asCCompiler::FindPropertyAccessor(const asCString &name, asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess) +{ + if( engine->ep.propertyAccessorMode == 0 ) + { + // Property accessors have been disabled by the application + return 0; + } + + int getId = 0, setId = 0; + asCString getName = "get_" + name; + asCString setName = "set_" + name; + asCArray multipleGetFuncs, multipleSetFuncs; + + if( ctx->type.dataType.IsObject() ) + { + asASSERT( ns == 0 ); + + // Don't look for property accessors in script classes if the script + // property accessors have been disabled by the application + if( !(ctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT) || + engine->ep.propertyAccessorMode == 2 ) + { + // Check if the object has any methods with the corresponding accessor name(s) + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]]; + // TODO: The type of the parameter should match the argument (unless the arg is a dummy) + if( f->name == getName && (int)f->parameterTypes.GetLength() == (arg?1:0) ) + { + if( getId == 0 ) + getId = ot->methods[n]; + else + { + if( multipleGetFuncs.GetLength() == 0 ) + multipleGetFuncs.PushLast(getId); + + multipleGetFuncs.PushLast(ot->methods[n]); + } + } + // TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref? + if( f->name == setName && (int)f->parameterTypes.GetLength() == (arg?2:1) ) + { + if( setId == 0 ) + setId = ot->methods[n]; + else + { + if( multipleSetFuncs.GetLength() == 0 ) + multipleSetFuncs.PushLast(setId); + + multipleSetFuncs.PushLast(ot->methods[n]); + } + } + } + } + } + else + { + asASSERT( ns != 0 ); + + // Look for appropriate global functions. + asCArray funcs; + asUINT n; + builder->GetFunctionDescriptions(getName.AddressOf(), funcs, ns); + for( n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *f = builder->GetFunctionDescription(funcs[n]); + // TODO: The type of the parameter should match the argument (unless the arg is a dummy) + if( (int)f->parameterTypes.GetLength() == (arg?1:0) ) + { + if( getId == 0 ) + getId = funcs[n]; + else + { + if( multipleGetFuncs.GetLength() == 0 ) + multipleGetFuncs.PushLast(getId); + + multipleGetFuncs.PushLast(funcs[n]); + } + } + } + + funcs.SetLength(0); + builder->GetFunctionDescriptions(setName.AddressOf(), funcs, ns); + for( n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *f = builder->GetFunctionDescription(funcs[n]); + // TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref? + if( (int)f->parameterTypes.GetLength() == (arg?2:1) ) + { + if( setId == 0 ) + setId = funcs[n]; + else + { + if( multipleSetFuncs.GetLength() == 0 ) + multipleSetFuncs.PushLast(getId); + + multipleSetFuncs.PushLast(funcs[n]); + } + } + } + } + + bool isConst = ctx->type.dataType.IsObjectConst(); + + // Check for multiple matches + if( multipleGetFuncs.GetLength() > 0 ) + { + // Filter the list by constness + FilterConst(multipleGetFuncs, !isConst); + + if( multipleGetFuncs.GetLength() > 1 ) + { + asCString str; + str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf()); + Error(str, node); + + PrintMatchingFuncs(multipleGetFuncs, node); + + return -1; + } + else + { + // The id may have changed + getId = multipleGetFuncs[0]; + } + } + + if( multipleSetFuncs.GetLength() > 0 ) + { + // Filter the list by constness + FilterConst(multipleSetFuncs, !isConst); + + if( multipleSetFuncs.GetLength() > 1 ) + { + asCString str; + str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf()); + Error(str, node); + + PrintMatchingFuncs(multipleSetFuncs, node); + + return -1; + } + else + { + // The id may have changed + setId = multipleSetFuncs[0]; + } + } + + // Check for type compatibility between get and set accessor + if( getId && setId ) + { + asCScriptFunction *getFunc = builder->GetFunctionDescription(getId); + asCScriptFunction *setFunc = builder->GetFunctionDescription(setId); + + // It is permitted for a getter to return a handle and the setter to take a reference + int idx = (arg?1:0); + if( !getFunc->returnType.IsEqualExceptRefAndConst(setFunc->parameterTypes[idx]) && + !((getFunc->returnType.IsObjectHandle() && !setFunc->parameterTypes[idx].IsObjectHandle()) && + (getFunc->returnType.GetTypeInfo() == setFunc->parameterTypes[idx].GetTypeInfo())) ) + { + asCString str; + str.Format(TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s, name.AddressOf()); + Error(str, node); + + asCArray funcs; + funcs.PushLast(getId); + funcs.PushLast(setId); + + PrintMatchingFuncs(funcs, node); + + return -1; + } + } + + // Check if we are within one of the accessors + int realGetId = getId; + int realSetId = setId; + if( outFunc->objectType && isThisAccess ) + { + // The property accessors would be virtual functions, so we need to find the real implementation + asCScriptFunction *getFunc = getId ? builder->GetFunctionDescription(getId) : 0; + if( getFunc && + getFunc->funcType == asFUNC_VIRTUAL && + outFunc->objectType->DerivesFrom(getFunc->objectType) ) + realGetId = outFunc->objectType->virtualFunctionTable[getFunc->vfTableIdx]->id; + asCScriptFunction *setFunc = setId ? builder->GetFunctionDescription(setId) : 0; + if( setFunc && + setFunc->funcType == asFUNC_VIRTUAL && + outFunc->objectType->DerivesFrom(setFunc->objectType) ) + realSetId = outFunc->objectType->virtualFunctionTable[setFunc->vfTableIdx]->id; + } + + // Avoid recursive call, by not treating this as a property accessor call. + // This will also allow having the real property with the same name as the accessors. + if( (isThisAccess || outFunc->objectType == 0) && + ((realGetId && realGetId == outFunc->id) || + (realSetId && realSetId == outFunc->id)) ) + { + getId = 0; + setId = 0; + } + + // Check if the application has disabled script written property accessors + if( engine->ep.propertyAccessorMode == 1 ) + { + if( getId && builder->GetFunctionDescription(getId)->funcType != asFUNC_SYSTEM ) + getId = 0; + if( setId && builder->GetFunctionDescription(setId)->funcType != asFUNC_SYSTEM ) + setId = 0; + } + + if( getId || setId ) + { + // Property accessors were found, but we don't know which is to be used yet, so + // we just prepare the bytecode for the method call, and then store the function ids + // so that the right one can be used when we get there. + ctx->property_get = getId; + ctx->property_set = setId; + + if( ctx->type.dataType.IsObject() ) + { + // If the object is read-only then we need to remember that + if( (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) || + (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) ) + ctx->property_const = true; + else + ctx->property_const = false; + + // If the object is a handle then we need to remember that + ctx->property_handle = ctx->type.dataType.IsObjectHandle(); + ctx->property_ref = ctx->type.dataType.IsReference(); + } + + // The setter's parameter type is used as the property type, + // unless only the getter is available + asCDataType dt; + if( setId ) + dt = builder->GetFunctionDescription(setId)->parameterTypes[(arg?1:0)]; + else + dt = builder->GetFunctionDescription(getId)->returnType; + + // Just change the type, the context must still maintain information + // about previous variable offset and the indicator of temporary variable. + int offset = ctx->type.stackOffset; + bool isTemp = ctx->type.isTemporary; + ctx->type.Set(dt); + ctx->type.stackOffset = (short)offset; + ctx->type.isTemporary = isTemp; + ctx->exprNode = node; + + // Store the argument for later use + if( arg ) + { + ctx->property_arg = asNEW(asCExprContext)(engine); + if( ctx->property_arg == 0 ) + { + // Out of memory + return -1; + } + + MergeExprBytecodeAndType(ctx->property_arg, arg); + } + + return 1; + } + + // No accessor was found + return 0; +} + +int asCCompiler::ProcessPropertySetAccessor(asCExprContext *ctx, asCExprContext *arg, asCScriptNode *node) +{ + // TODO: A lot of this code is similar to ProcessPropertyGetAccessor. Can we unify them? + + if( !ctx->property_set ) + { + Error(TXT_PROPERTY_HAS_NO_SET_ACCESSOR, node); + return -1; + } + + asCScriptFunction *func = builder->GetFunctionDescription(ctx->property_set); + + // Make sure the arg match the property + asCArray funcs; + funcs.PushLast(ctx->property_set); + asCArray args; + if( ctx->property_arg ) + args.PushLast(ctx->property_arg); + args.PushLast(arg); + MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const); + if( funcs.GetLength() == 0 ) + { + // MatchFunctions already reported the error + if( ctx->property_arg ) + { + asDELETE(ctx->property_arg, asCExprContext); + ctx->property_arg = 0; + } + return -1; + } + + if( func->objectType ) + { + // Setup the context with the original type so the method call gets built correctly + ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const); + if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true); + if( ctx->property_ref ) ctx->type.dataType.MakeReference(true); + + // Don't allow the call if the object is read-only and the property accessor is not const + if( ctx->property_const && !func->isReadOnly ) + { + Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node); + asCArray funcCandidates; + funcCandidates.PushLast(ctx->property_set); + PrintMatchingFuncs(funcCandidates, node); + } + } + + // Call the accessor + MakeFunctionCall(ctx, ctx->property_set, func->objectType, args, node); + + ctx->property_get = 0; + ctx->property_set = 0; + if( ctx->property_arg ) + { + asDELETE(ctx->property_arg, asCExprContext); + ctx->property_arg = 0; + } + + return 0; +} + +int asCCompiler::ProcessPropertyGetSetAccessor(asCExprContext *ctx, asCExprContext *lctx, asCExprContext *rctx, eTokenType op, asCScriptNode *errNode) +{ + // TODO: Perhaps it might be interesting to allow the definition of compound setters for better + // performance, e.g. set_add_prop, set_mul_prop, etc. With these it would also be possible + // to support value types, since it would be a single call + + // Compound assignment for indexed property accessors is not supported yet + if( lctx->property_arg != 0 ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_WITH_IDX_PROP, errNode); + return -1; + } + + // Compound assignments require both get and set accessors + if( lctx->property_set == 0 || lctx->property_get == 0 ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_REQUIRE_GET_SET, errNode); + return -1; + } + + // Property accessors on value types (or scoped references types) are not supported since + // it is not possible to guarantee that the object will stay alive between the two calls + asCScriptFunction *func = engine->scriptFunctions[lctx->property_set]; + if( func->objectType && (func->objectType->flags & (asOBJ_VALUE | asOBJ_SCOPED)) ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_ON_VALUE_TYPE, errNode); + return -1; + } + + // Translate the compound assignment to the corresponding dual operator + switch( op ) + { + case ttAddAssign: op = ttPlus; break; + case ttSubAssign: op = ttMinus; break; + case ttMulAssign: op = ttStar; break; + case ttDivAssign: op = ttSlash; break; + case ttModAssign: op = ttPercent; break; + case ttPowAssign: op = ttStarStar; break; + + case ttAndAssign: op = ttAmp; break; + case ttOrAssign: op = ttBitOr; break; + case ttXorAssign: op = ttBitXor; break; + + case ttShiftLeftAssign: op = ttBitShiftLeft; break; + case ttShiftRightAAssign: op = ttBitShiftRightArith; break; + case ttShiftRightLAssign: op = ttBitShiftRight; break; + + default: op = ttUnrecognizedToken; break; + } + + if( op == ttUnrecognizedToken ) + { + // Shouldn't happen + asASSERT(false); + + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + return -1; + } + + asCExprContext before(engine); + if( func->objectType && (func->objectType->flags & (asOBJ_REF|asOBJ_SCOPED)) == asOBJ_REF ) + { + // Keep a reference to the object in a local variable + before.bc.AddCode(&lctx->bc); + + asUINT len = reservedVariables.GetLength(); + rctx->bc.GetVarsUsed(reservedVariables); + before.bc.GetVarsUsed(reservedVariables); + + asCDataType dt = asCDataType::CreateObjectHandle(func->objectType, false); + int offset = AllocateVariable(dt, true); + + reservedVariables.SetLength(len); + + before.type.SetVariable(dt, offset, true); + + if( lctx->property_ref ) + before.bc.Instr(asBC_RDSPtr); + before.bc.InstrSHORT(asBC_PSF, (short)offset); + before.bc.InstrPTR(asBC_REFCPY, func->objectType); + before.bc.Instr(asBC_PopPtr); + + if( lctx->type.isTemporary ) + { + // Add the release of the temporary variable as a deferred expression + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, lctx->type.stackOffset, true); + before.deferredParams.PushLast(deferred); + } + + // Update the left expression to use the local variable + lctx->bc.InstrSHORT(asBC_PSF, (short)offset); + lctx->type.stackOffset = (short)offset; + lctx->property_ref = true; + + // Don't release the temporary variable too early + lctx->type.isTemporary = false; + + ctx->bc.AddCode(&before.bc); + } + + // Keep the original information on the property + asCExprContext llctx(engine); + llctx.type = lctx->type; + llctx.property_arg = lctx->property_arg; + llctx.property_const = lctx->property_const; + llctx.property_get = lctx->property_get; + llctx.property_handle = lctx->property_handle; + llctx.property_ref = lctx->property_ref; + llctx.property_set = lctx->property_set; + + // Compile the dual operator using the get accessor + CompileOperator(errNode, lctx, rctx, ctx, op, false); + + // If we made a local variable to hold the reference it must be reused + if( before.type.stackOffset ) + llctx.bc.InstrSHORT(asBC_PSF, before.type.stackOffset); + + // Compile the assignment using the set accessor + ProcessPropertySetAccessor(&llctx, ctx, errNode); + + MergeExprBytecodeAndType(ctx, &llctx); + + if( before.type.stackOffset ) + ReleaseTemporaryVariable(before.type.stackOffset, &ctx->bc); + + asASSERT( ctx->deferredParams.GetLength() == 0 ); + ctx->deferredParams = before.deferredParams; + ProcessDeferredParams(ctx); + + return 0; +} + +void asCCompiler::ProcessPropertyGetAccessor(asCExprContext *ctx, asCScriptNode *node) +{ + // If no property accessor has been prepared then don't do anything + if( !ctx->property_get && !ctx->property_set ) + return; + + if( !ctx->property_get ) + { + // Raise error on missing accessor + Error(TXT_PROPERTY_HAS_NO_GET_ACCESSOR, node); + ctx->type.SetDummy(); + return; + } + + asCExprValue objType = ctx->type; + asCScriptFunction *func = builder->GetFunctionDescription(ctx->property_get); + + // Make sure the arg match the property + asCArray funcs; + funcs.PushLast(ctx->property_get); + asCArray args; + if( ctx->property_arg ) + args.PushLast(ctx->property_arg); + MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const); + if( funcs.GetLength() == 0 ) + { + // MatchFunctions already reported the error + if( ctx->property_arg ) + { + asDELETE(ctx->property_arg, asCExprContext); + ctx->property_arg = 0; + } + ctx->type.SetDummy(); + return; + } + + if( func->objectType ) + { + // Setup the context with the original type so the method call gets built correctly + ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const); + if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true); + if( ctx->property_ref ) ctx->type.dataType.MakeReference(true); + + // Don't allow the call if the object is read-only and the property accessor is not const + if( ctx->property_const && !func->isReadOnly ) + { + Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node); + asCArray funcCandidates; + funcCandidates.PushLast(ctx->property_get); + PrintMatchingFuncs(funcCandidates, node); + } + } + + // The explicit handle flag must be remembered + bool isExplicitHandle = ctx->type.isExplicitHandle; + + // Call the accessor + MakeFunctionCall(ctx, ctx->property_get, func->objectType, args, node); + if( isExplicitHandle ) + ctx->type.isExplicitHandle = true; + + // Clear the property get/set ids + ctx->property_get = 0; + ctx->property_set = 0; + if( ctx->property_arg ) + { + asDELETE(ctx->property_arg, asCExprContext); + ctx->property_arg = 0; + } +} + +int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asCExprContext *ctx) +{ + // Don't allow any postfix operators on expressions that take address of class method + if( ctx->IsClassMethod() ) + { + Error(TXT_INVALID_OP_ON_METHOD, node); + return -1; + } + + // Don't allow any operators on void expressions + if( ctx->IsVoidExpression() ) + { + Error(TXT_VOID_CANT_BE_OPERAND, node); + return -1; + } + + // Check if the variable is initialized (if it indeed is a variable) + IsVariableInitialized(&ctx->type, node); + + int op = node->tokenType; + if( (op == ttInc || op == ttDec) && ctx->type.dataType.IsObject() ) + { + const char *opName = 0; + switch( op ) + { + case ttInc: opName = "opPostInc"; break; + case ttDec: opName = "opPostDec"; break; + } + + if( opName ) + { + // TODO: Should convert this to something similar to CompileOverloadedDualOperator2 + ProcessPropertyGetAccessor(ctx, node); + + // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method + + // Find the correct method + bool isConst = ctx->type.dataType.IsObjectConst(); + asCArray funcs; + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == opName && + func->parameterTypes.GetLength() == 0 && + (!isConst || func->isReadOnly) ) + { + funcs.PushLast(func->id); + } + } + + // Did we find the method? + if( funcs.GetLength() == 1 ) + { + asCArray args; + MakeFunctionCall(ctx, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + return 0; + } + else if( funcs.GetLength() == 0 ) + { + asCString str; + str = asCString(opName) + "()"; + if( isConst ) + str += " const"; + str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return -1; + } + else if( funcs.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(funcs, node); + + ctx->type.SetDummy(); + return -1; + } + } + } + else if( op == ttInc || op == ttDec ) + { + // Make sure the reference isn't a temporary variable + if( ctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, node); + return -1; + } + if( ctx->type.dataType.IsReadOnly() ) + { + Error(TXT_REF_IS_READ_ONLY, node); + return -1; + } + if( ctx->property_get || ctx->property_set ) + { + Error(TXT_INVALID_REF_PROP_ACCESS, node); + return -1; + } + if( !ctx->type.isLValue ) + { + Error(TXT_NOT_LVALUE, node); + return -1; + } + + if( ctx->type.isVariable && !ctx->type.dataType.IsReference() ) + ConvertToReference(ctx); + else if( !ctx->type.dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + // Copy the value to a temp before changing it + ConvertToTempVariable(ctx); + asASSERT(!ctx->type.isLValue); + + // Increment the value pointed to by the reference still in the register + asEBCInstr iInc = asBC_INCi, iDec = asBC_DECi; + if( ctx->type.dataType.IsDoubleType() ) + { + iInc = asBC_INCd; + iDec = asBC_DECd; + } + else if( ctx->type.dataType.IsFloatType() ) + { + iInc = asBC_INCf; + iDec = asBC_DECf; + } + else if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) ) + { + iInc = asBC_INCi16; + iDec = asBC_DECi16; + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) ) + { + iInc = asBC_INCi8; + iDec = asBC_DECi8; + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) ) + { + iInc = asBC_INCi64; + iDec = asBC_DECi64; + } + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + if( op == ttInc ) ctx->bc.Instr(iInc); else ctx->bc.Instr(iDec); + } + else if( op == ttDot ) + { + if( node->firstChild->nodeType == snIdentifier ) + { + ProcessPropertyGetAccessor(ctx, node); + + // Get the property name + asCString name(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength); + + if( ctx->type.dataType.IsObject() ) + { + // We need to look for get/set property accessors. + // If found, the context stores information on the get/set accessors + // until it is known which is to be used. + int r = 0; + if( node->next && node->next->tokenType == ttOpenBracket ) + { + // The property accessor should take an index arg + asCExprContext dummyArg(engine); + r = FindPropertyAccessor(name, ctx, &dummyArg, node, 0); + } + if( r == 0 ) + r = FindPropertyAccessor(name, ctx, node, 0); + if( r != 0 ) + return r; + + if( !ctx->type.dataType.IsPrimitive() ) + Dereference(ctx, true); + + if( ctx->type.dataType.IsObjectHandle() ) + { + // Convert the handle to a normal object + asCDataType dt = ctx->type.dataType; + dt.MakeHandle(false); + + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV); + + // The handle may not have been an lvalue, but the dereferenced object is + ctx->type.isLValue = true; + } + + bool isConst = ctx->type.dataType.IsObjectConst(); + + asCObjectProperty *prop = builder->GetObjectProperty(ctx->type.dataType, name.AddressOf()); + if( prop ) + { + // Is the property access allowed? + if( (prop->isPrivate || prop->isProtected) && (!outFunc || outFunc->objectType != ctx->type.dataType.GetTypeInfo()) ) + { + asCString msg; + if( prop->isPrivate ) + msg.Format(TXT_PRIVATE_PROP_ACCESS_s, name.AddressOf()); + else + msg.Format(TXT_PROTECTED_PROP_ACCESS_s, name.AddressOf()); + Error(msg, node); + } + + // Put the offset on the stack + ctx->bc.InstrSHORT_DW(asBC_ADDSi, (short)prop->byteOffset, engine->GetTypeIdFromDataType(asCDataType::CreateType(ctx->type.dataType.GetTypeInfo(), false))); + + if( prop->type.IsReference() ) + ctx->bc.Instr(asBC_RDSPtr); + + // Reference to primitive must be stored in the temp register + if( prop->type.IsPrimitive() ) + { + ctx->bc.Instr(asBC_PopRPtr); + } + + // Keep information about temporary variables as deferred expression + if( ctx->type.isTemporary ) + { + // Add the release of this reference, as a deferred expression + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, ctx->type.stackOffset, true); + + ctx->deferredParams.PushLast(deferred); + } + + // Set the new type and make sure it is not treated as a variable anymore + ctx->type.dataType = prop->type; + ctx->type.dataType.MakeReference(true); + ctx->type.isVariable = false; + ctx->type.isTemporary = false; + + if( (ctx->type.dataType.IsObject() || ctx->type.dataType.IsFuncdef()) && !ctx->type.dataType.IsObjectHandle() ) + { + // Objects that are members are not references + ctx->type.dataType.MakeReference(false); + + // The object is safe (life time guaranteed) if the parent object is also safe + } + else if (ctx->type.dataType.IsObjectHandle()) + { + // A object accessed through a handle cannot be considered safe, + // as it can be cleared at any time + ctx->type.isHandleSafe = false; + } + + ctx->type.dataType.MakeReadOnly(isConst ? true : prop->type.IsReadOnly()); + } + else + { + // If the name is not a property, the compiler must check if the name matches + // a method, which can be used for constructing delegates + asIScriptFunction *func = 0; + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[ot->methods[n]]->name == name ) + { + func = engine->scriptFunctions[ot->methods[n]]; + break; + } + } + + if( func ) + { + // An object method was found. Keep the name of the method in the expression, but + // don't actually modify the bytecode at this point since it is not yet known what + // the method will be used for, or even what overloaded method should be used. + ctx->methodName = name; + } + else + { + asCString str; + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + return -1; + } + } + } + else + { + asCString str; + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + return -1; + } + } + else + { + // Make sure it is an object we are accessing + if( !ctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + return -1; + } + + // Process the get property accessor + ProcessPropertyGetAccessor(ctx, node); + + // Compile function call + int r = CompileFunctionCall(node->firstChild, ctx, CastToObjectType(ctx->type.dataType.GetTypeInfo()), ctx->type.dataType.IsObjectConst()); + if( r < 0 ) return r; + } + } + else if( op == ttOpenBracket ) + { + // If the property access takes an index arg and the argument hasn't been evaluated yet, + // then we should use that instead of processing it now. If the argument has already been + // evaluated, then we should process the property accessor as a get access now as the new + // index operator is on the result of that accessor. + asCString propertyName; + asSNameSpace *ns = 0; + if( ((ctx->property_get && builder->GetFunctionDescription(ctx->property_get)->GetParamCount() == 1) || + (ctx->property_set && builder->GetFunctionDescription(ctx->property_set)->GetParamCount() == 2)) && + (ctx->property_arg && ctx->property_arg->type.dataType.GetTokenType() == ttUnrecognizedToken) ) + { + // Determine the name of the property accessor + asCScriptFunction *func = 0; + if( ctx->property_get ) + func = builder->GetFunctionDescription(ctx->property_get); + else + func = builder->GetFunctionDescription(ctx->property_set); + propertyName = func->GetName(); + propertyName = propertyName.SubString(4); + + // Set the original type of the expression so we can re-evaluate the property accessor + if( func->objectType ) + { + ctx->type.dataType = asCDataType::CreateType(func->objectType, ctx->property_const); + if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true); + if( ctx->property_ref ) ctx->type.dataType.MakeReference(true); + } + else + { + // Store the namespace where the function is declared + // so the same function can be found later + ctx->type.SetDummy(); + ns = func->nameSpace; + } + + ctx->property_get = ctx->property_set = 0; + if( ctx->property_arg ) + { + asDELETE(ctx->property_arg, asCExprContext); + ctx->property_arg = 0; + } + } + else + { + if( !ctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + return -1; + } + + ProcessPropertyGetAccessor(ctx, node); + } + + // Compile the expression + bool isOK = true; + asCArray args; + asCArray namedArgs; + asASSERT( node->firstChild->nodeType == snArgList ); + if( CompileArgumentList(node->firstChild, args, namedArgs) >= 0 ) + { + // Check for the existence of the opIndex method + bool lookForProperty = true; + if( propertyName == "" ) + { + bool isConst = ctx->type.dataType.IsObjectConst(); + asCObjectType *objectType = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + + asCArray funcs; + builder->GetObjectMethodDescriptions("opIndex", objectType, funcs, isConst); + if( funcs.GetLength() > 0 ) + { + // Since there are opIndex methods, the compiler should not look for get/set_opIndex accessors + lookForProperty = false; + + // Determine which of opIndex methods that match + MatchFunctions(funcs, args, node, "opIndex", 0, objectType, isConst); + if( funcs.GetLength() != 1 ) + { + // The error has already been reported by MatchFunctions + isOK = false; + } + else + { + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType); + + if( r == 0 ) + MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, ctx->type.stackOffset); + else + isOK = false; + } + } + } + if( lookForProperty && isOK ) + { + if( args.GetLength() != 1 ) + { + // TODO: opIndex: Implement this + Error("Property accessor with index only support 1 index argument for now", node); + isOK = false; + } + + Dereference(ctx, true); + asCExprContext lctx(engine); + MergeExprBytecodeAndType(&lctx, ctx); + + // Check for accessors methods for the opIndex, either as get/set_opIndex or as get/set with the property name + int r = FindPropertyAccessor(propertyName == "" ? "opIndex" : propertyName.AddressOf(), &lctx, args[0], node, ns); + if( r == 0 ) + { + asCString str; + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + isOK = false; + } + else if( r < 0 ) + isOK = false; + + if( isOK ) + MergeExprBytecodeAndType(ctx, &lctx); + } + } + else + isOK = false; + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + + if( !isOK ) + return -1; + } + else if( op == ttOpenParanthesis ) + { + // TODO: Most of this is already done by CompileFunctionCall(). Can we share the code? + + // Make sure the expression is a funcdef or an object that may have opCall methods + if( !ctx->type.dataType.GetTypeInfo() || (!ctx->type.dataType.IsFuncdef() && !ctx->type.dataType.IsObject()) ) + { + Error(TXT_EXPR_DOESNT_EVAL_TO_FUNC, node); + return -1; + } + + // Compile arguments + asCArray args; + asCArray namedArgs; + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) + { + // Match arguments with the funcdef + asCArray funcs; + if( ctx->type.dataType.IsFuncdef() ) + { + funcs.PushLast(CastToFuncdefType(ctx->type.dataType.GetTypeInfo())->funcdef->id); + MatchFunctions(funcs, args, node, ctx->type.dataType.GetTypeInfo()->name.AddressOf(), &namedArgs); + } + else + { + bool isConst = ctx->type.dataType.IsObjectConst(); + + builder->GetObjectMethodDescriptions("opCall", CastToObjectType(ctx->type.dataType.GetTypeInfo()), funcs, isConst); + MatchFunctions(funcs, args, node, "opCall", &namedArgs, CastToObjectType(ctx->type.dataType.GetTypeInfo()), isConst); + } + + if( funcs.GetLength() != 1 ) + { + // The error was reported by MatchFunctions() + + // Dummy value + ctx->type.SetDummy(); + } + else + { + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), &namedArgs); + + // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or + // is it enough to make sure it is in a local variable? + + // For function pointer we must guarantee that the function is safe, i.e. + // by first storing the function pointer in a local variable (if it isn't already in one) + if( r == asSUCCESS ) + { + Dereference(ctx, true); + if( ctx->type.dataType.IsFuncdef() ) + { + if( !ctx->type.isVariable ) + ConvertToVariable(ctx); + + // Remove the reference from the stack as the asBC_CALLPTR instruction takes the variable as argument + ctx->bc.Instr(asBC_PopPtr); + } + + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.IsFuncdef() ? 0 : CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node, false, 0, ctx->type.stackOffset); + } + } + } + else + ctx->type.SetDummy(); + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n], asCExprContext); + } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx, asCExprContext); + } + } + + return 0; +} + +int asCCompiler::GetPrecedence(asCScriptNode *op) +{ + // x ** y + // x * y, x / y, x % y + // x + y, x - y + // x <= y, x < y, x >= y, x > y + // x = =y, x != y, x xor y, x is y, x !is y + // x and y + // x or y + + // The following are not used in this function, + // but should have lower precedence than the above + // x ? y : z + // x = y + + // The expression term have the highest precedence + if( op->nodeType == snExprTerm ) + return 1; + + // Evaluate operators by token + int tokenType = op->tokenType; + if( tokenType == ttStarStar ) + return 0; + + if( tokenType == ttStar || tokenType == ttSlash || tokenType == ttPercent ) + return -1; + + if( tokenType == ttPlus || tokenType == ttMinus ) + return -2; + + if( tokenType == ttBitShiftLeft || + tokenType == ttBitShiftRight || + tokenType == ttBitShiftRightArith ) + return -3; + + if( tokenType == ttAmp ) + return -4; + + if( tokenType == ttBitXor ) + return -5; + + if( tokenType == ttBitOr ) + return -6; + + if( tokenType == ttLessThanOrEqual || + tokenType == ttLessThan || + tokenType == ttGreaterThanOrEqual || + tokenType == ttGreaterThan ) + return -7; + + if( tokenType == ttEqual || tokenType == ttNotEqual || tokenType == ttXor || tokenType == ttIs || tokenType == ttNotIs ) + return -8; + + if( tokenType == ttAnd ) + return -9; + + if( tokenType == ttOr ) + return -10; + + // Unknown operator + asASSERT(false); + + return 0; +} + +asUINT asCCompiler::MatchArgument(asCArray &funcs, asCArray &matches, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct) +{ + matches.SetLength(0); + + for( asUINT n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]); + + // Does the function have arguments enough? + if( (int)desc->parameterTypes.GetLength() <= paramNum ) + continue; + + int cost = MatchArgument(desc, argExpr, paramNum, allowObjectConstruct); + if( cost != -1 ) + matches.PushLast(asSOverloadCandidate(funcs[n], asUINT(cost))); + } + + return (asUINT)matches.GetLength(); +} + +int asCCompiler::MatchArgument(asCScriptFunction *desc, const asCExprContext *argExpr, int paramNum, bool allowObjectConstruct) +{ + // void expressions can match any out parameter, but nothing else + if( argExpr->IsVoidExpression() ) + { + if( desc->inOutFlags[paramNum] == asTM_OUTREF ) + return 0; + return -1; + } + + // Can we make the match by implicit conversion? + asCExprContext ti(engine); + ti.type = argExpr->type; + ti.methodName = argExpr->methodName; + ti.enumValue = argExpr->enumValue; + ti.exprNode = argExpr->exprNode; + if( argExpr->type.dataType.IsPrimitive() ) + ti.type.dataType.MakeReference(false); + + // Don't allow the implicit conversion to make a copy in case the argument is expecting a reference to the true value + if (desc->parameterTypes[paramNum].IsReference() && desc->inOutFlags[paramNum] == asTM_INOUTREF) + allowObjectConstruct = false; + + int cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct); + + // If the function parameter is an inout-reference then it must not be possible to call the + // function with an incorrect argument type, even though the type can normally be converted. + if( desc->parameterTypes[paramNum].IsReference() && + desc->inOutFlags[paramNum] == asTM_INOUTREF && + desc->parameterTypes[paramNum].GetTokenType() != ttQuestion ) + { + // Observe, that the below checks are only necessary for when unsafe references have been + // enabled by the application. Without this the &inout reference form wouldn't be allowed + // for these value types. + + // Don't allow a primitive to be converted to a reference of another primitive type + if( desc->parameterTypes[paramNum].IsPrimitive() && + desc->parameterTypes[paramNum].GetTokenType() != argExpr->type.dataType.GetTokenType() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow an enum to be converted to a reference of another enum type + if( desc->parameterTypes[paramNum].IsEnumType() && + desc->parameterTypes[paramNum].GetTypeInfo() != argExpr->type.dataType.GetTypeInfo() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow a non-handle expression to be converted to a reference to a handle + if( desc->parameterTypes[paramNum].IsObjectHandle() && + !argExpr->type.dataType.IsObjectHandle() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow a value type to be converted + if( (desc->parameterTypes[paramNum].GetTypeInfo() && (desc->parameterTypes[paramNum].GetTypeInfo()->GetFlags() & asOBJ_VALUE)) && + (desc->parameterTypes[paramNum].GetTypeInfo() != argExpr->type.dataType.GetTypeInfo()) ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + } + + // How well does the argument match the function parameter? + if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) ) + return cost; + + // No match is available + return -1; +} + +void asCCompiler::PrepareArgument2(asCExprContext *ctx, asCExprContext *arg, asCDataType *paramType, bool isFunction, int refType, bool isMakingCopy) +{ + // Reference parameters whose value won't be used don't evaluate the expression + // Clean arguments (i.e. default value) will be passed in directly as there is nothing to protect + if( paramType->IsReference() && !(refType & asTM_INREF) && !arg->isCleanArg ) + { + // Store the original bytecode so that it can be reused when processing the deferred output parameter + asCExprContext *orig = asNEW(asCExprContext)(engine); + if( orig == 0 ) + { + // Out of memory + return; + } + MergeExprBytecodeAndType(orig, arg); + arg->origExpr = orig; + } + + PrepareArgument(paramType, arg, arg->exprNode, isFunction, refType, isMakingCopy); + + // arg still holds the original expression for output parameters + ctx->bc.AddCode(&arg->bc); +} + +bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, bool leftToRight, asCExprContext *ctx, bool isHandle, eTokenType token) +{ + DetermineSingleFunc(lctx, node); + DetermineSingleFunc(rctx, node); + + ctx->exprNode = node; + + // What type of operator is it? + if( token == ttUnrecognizedToken ) + token = node->tokenType; + if( token == ttUnrecognizedToken ) + { + // This happens when the compiler is inferring an assignment + // operation from another action, for example in preparing a value + // as a function argument + token = ttAssignment; + } + + // boolean operators are not overloadable + if( token == ttAnd || + token == ttOr || + token == ttXor ) + return false; + + // Dual operators can also be implemented as class methods + if( token == ttEqual || + token == ttNotEqual ) + { + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching opEquals method + int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, leftToRight, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + if( r == 0 ) + { + // Try again by switching the order of the operands + r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, !leftToRight, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + } + + if( r == 1 ) + { + if( token == ttNotEqual ) + ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); + + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + return true; + } + } + + if( token == ttEqual || + token == ttNotEqual || + token == ttLessThan || + token == ttLessThanOrEqual || + token == ttGreaterThan || + token == ttGreaterThanOrEqual ) + { + bool swappedOrder = false; + + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching opCmp method + int r = CompileOverloadedDualOperator2(node, "opCmp", lctx, rctx, leftToRight, ctx, true, asCDataType::CreatePrimitive(ttInt, false)); + if( r == 0 ) + { + // Try again by switching the order of the operands + swappedOrder = true; + r = CompileOverloadedDualOperator2(node, "opCmp", rctx, lctx, !leftToRight, ctx, true, asCDataType::CreatePrimitive(ttInt, false)); + } + + if( r == 1 ) + { + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true); + + ctx->bc.InstrW_DW(asBC_CMPIi, ctx->type.stackOffset, 0); + + if( token == ttEqual ) + ctx->bc.Instr(asBC_TZ); + else if( token == ttNotEqual ) + ctx->bc.Instr(asBC_TNZ); + else if( (token == ttLessThan && !swappedOrder) || + (token == ttGreaterThan && swappedOrder) ) + ctx->bc.Instr(asBC_TS); + else if( (token == ttLessThanOrEqual && !swappedOrder) || + (token == ttGreaterThanOrEqual && swappedOrder) ) + ctx->bc.Instr(asBC_TNP); + else if( (token == ttGreaterThan && !swappedOrder) || + (token == ttLessThan && swappedOrder) ) + ctx->bc.Instr(asBC_TP); + else if( (token == ttGreaterThanOrEqual && !swappedOrder) || + (token == ttLessThanOrEqual && swappedOrder) ) + ctx->bc.Instr(asBC_TNS); + + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), a, true); + + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + #if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + #else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + #endif + return true; + } + } + + // The rest of the operators are not commutative, and doesn't require specific return type + const char *op = 0, *op_r = 0; + switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested + { + case ttPlus: op = "opAdd"; op_r = "opAdd_r"; break; + case ttMinus: op = "opSub"; op_r = "opSub_r"; break; + case ttStar: op = "opMul"; op_r = "opMul_r"; break; + case ttSlash: op = "opDiv"; op_r = "opDiv_r"; break; + case ttPercent: op = "opMod"; op_r = "opMod_r"; break; + case ttStarStar: op = "opPow"; op_r = "opPow_r"; break; + case ttBitOr: op = "opOr"; op_r = "opOr_r"; break; + case ttAmp: op = "opAnd"; op_r = "opAnd_r"; break; + case ttBitXor: op = "opXor"; op_r = "opXor_r"; break; + case ttBitShiftLeft: op = "opShl"; op_r = "opShl_r"; break; + case ttBitShiftRight: op = "opShr"; op_r = "opShr_r"; break; + case ttBitShiftRightArith: op = "opUShr"; op_r = "opUShr_r"; break; + } + + // TODO: Might be interesting to support a concatenation operator, e.g. ~ + + if( op && op_r ) + { + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching operator method + int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, leftToRight, ctx); + if( r == 0 ) + { + // Try again by switching the order of the operands, and using the reversed operator + r = CompileOverloadedDualOperator2(node, op_r, rctx, lctx, !leftToRight, ctx); + } + + if( r == 1 ) + { + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetDummy(); + return true; + } + } + + // Assignment operators + op = 0; + if( isHandle ) + { + // Only asOBJ_ASHANDLE types can get here + asASSERT( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) ); + asASSERT( token == ttAssignment ); + + if( token == ttAssignment ) + op = "opHndlAssign"; + } + else + { + switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested + { + case ttAssignment: op = "opAssign"; break; + case ttAddAssign: op = "opAddAssign"; break; + case ttSubAssign: op = "opSubAssign"; break; + case ttMulAssign: op = "opMulAssign"; break; + case ttDivAssign: op = "opDivAssign"; break; + case ttModAssign: op = "opModAssign"; break; + case ttPowAssign: op = "opPowAssign"; break; + case ttOrAssign: op = "opOrAssign"; break; + case ttAndAssign: op = "opAndAssign"; break; + case ttXorAssign: op = "opXorAssign"; break; + case ttShiftLeftAssign: op = "opShlAssign"; break; + case ttShiftRightLAssign: op = "opShrAssign"; break; + case ttShiftRightAAssign: op = "opUShrAssign"; break; + } + } + + if( op ) + { + if( builder->engine->ep.disallowValueAssignForRefType && + lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_REF) && !(lctx->type.dataType.GetTypeInfo()->flags & asOBJ_SCOPED) ) + { + if( token == ttAssignment ) + Error(TXT_DISALLOW_ASSIGN_ON_REF_TYPE, node); + else + Error(TXT_DISALLOW_COMPOUND_ASSIGN_ON_REF_TYPE, node); + + // Set a dummy output + ctx->type.Set(lctx->type.dataType); + return true; + } + + // TODO: Shouldn't accept const lvalue with the assignment operators + + // Find the matching operator method + int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, false, ctx); + if( r == 1 ) + { + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetDummy(); + return true; + } + } + + // No suitable operator was found + return false; +} + +// Returns negative on compile error +// zero on no matching operator +// one on matching operator +int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asCExprContext *lctx, asCExprContext *rctx, bool leftToRight, asCExprContext *ctx, bool specificReturn, const asCDataType &returnType) +{ + // Find the matching method + if( lctx->type.dataType.IsObject() && + (!lctx->type.isExplicitHandle || + lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE) ) + { + asUINT n; + + // Is the left value a const? + bool isConst = lctx->type.dataType.IsObjectConst(); + + asCArray funcs; + asCObjectType *ot = CastToObjectType(lctx->type.dataType.GetTypeInfo()); + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + asASSERT( func ); + if( func && func->name == methodName && + (!specificReturn || func->returnType == returnType) && + func->parameterTypes.GetLength() == 1 && + (!isConst || func->isReadOnly) ) + { + // Make sure the method is accessible by the module + if( builder->module->accessMask & func->accessMask ) + { + funcs.PushLast(func->id); + } + } + } + + // Which is the best matching function? + asCArray tempFuncs; + MatchArgument(funcs, tempFuncs, rctx, 0); + + // Find the lowest cost operator(s) + asCArray ops; + asUINT bestCost = asUINT(-1); + for( n = 0; n < tempFuncs.GetLength(); ++n ) + { + asUINT cost = tempFuncs[n].cost; + if( cost < bestCost ) + { + ops.SetLength(0); + bestCost = cost; + } + if( cost == bestCost ) + ops.PushLast(tempFuncs[n].funcId); + } + + // If the object is not const, then we need to prioritize non-const methods + if( !isConst ) + FilterConst(ops); + + // Did we find an operator? + if( ops.GetLength() == 1 ) + { + // Process the lctx expression as get accessor + ProcessPropertyGetAccessor(lctx, node); + + asCExprContext tmpCtx(engine); + if (leftToRight) + { + // Make sure lctx is in fact a variable. If it is a reference there is no + // guarantee that the reference will stay alive throughout the evaluation of rctx + if (!lctx->type.isVariable) + { + // Reserve the variables used in the right expression so the new temporary + // variable allocated for the left operand isn't accidentally overwritten. + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + + if (lctx->type.dataType.SupportHandles()) + lctx->type.dataType.MakeHandle(true); + PrepareTemporaryVariable(node, lctx); + + reservedVariables.SetLength(l); + } + + // Move the bytecode for the left operand to a temporary context + // so we can later make sure this is computed first + tmpCtx.bc.AddCode(&lctx->bc); + tmpCtx.bc.Instr(asBC_PopPtr); + + // Add bytecode to push the object pointer computed in the left operand on the stack as the this pointer + // This will be placed after rctx by MakeFunctionCall below + lctx->bc.InstrWORD(asBC_PSF, lctx->type.stackOffset); + + // Implicitly dereference handle parameters sent by reference + sVariable *v = variables->GetVariableByOffset(lctx->type.stackOffset); + if (v && v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle())) + lctx->bc.Instr(asBC_RDSPtr); + } + else + { + // Make sure the rvalue doesn't have deferred temporary variables that are also used in the lvalue, + // since that would cause the VM to overwrite the variable while executing the bytecode for the lvalue. + asCArray usedVars; + lctx->bc.GetVarsUsed(usedVars); + asUINT oldReservedVars = reservedVariables.GetLength(); + for (n = 0; n < rctx->deferredParams.GetLength(); n++) + { + if (rctx->deferredParams[n].argType.isTemporary && + usedVars.Exists(rctx->deferredParams[n].argType.stackOffset)) + { + if (reservedVariables.GetLength() == oldReservedVars) + reservedVariables.Concatenate(usedVars); + + // Allocate a new variable for the deferred argument + int offset = AllocateVariableNotIn(rctx->deferredParams[n].argType.dataType, true, false, rctx); + int oldVar = rctx->deferredParams[n].argType.stackOffset; + rctx->deferredParams[n].argType.stackOffset = short(offset); + rctx->bc.ExchangeVar(oldVar, offset); + ReleaseTemporaryVariable(oldVar, 0); + } + } + reservedVariables.SetLength(oldReservedVars); + } + + // Merge the bytecode so that it forms lvalue.methodName(rvalue) + asCArray args; + args.PushLast(rctx); + MergeExprBytecode(ctx, lctx); + ctx->type = lctx->type; + MakeFunctionCall(ctx, ops[0], CastToObjectType(ctx->type.dataType.GetTypeInfo()), args, node); + + // Rearrange the bytecode so the left argument is computed first + if (leftToRight) + { + tmpCtx.bc.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpCtx.bc); + } + + // Found matching operator + return 1; + } + else if( ops.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(ops, node); + + ctx->type.SetDummy(); + + // Compiler error + return -1; + } + } + + // No matching operator + return 0; +} + +void asCCompiler::MakeFunctionCall(asCExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable, int stackOffset, int funcPtrVar) +{ + if( objectType ) + Dereference(ctx, true); + + // Store the expression node for error reporting + if( ctx->exprNode == 0 ) + ctx->exprNode = node; + + asCByteCode objBC(engine); + objBC.AddCode(&ctx->bc); + + PrepareFunctionCall(funcId, &ctx->bc, args); + + // Verify if any of the args variable offsets are used in the other code. + // If they are exchange the offset for a new one + asUINT n; + for( n = 0; n < args.GetLength(); n++ ) + { + if( args[n]->type.isTemporary && objBC.IsVarUsed(args[n]->type.stackOffset) ) + { + // Release the current temporary variable + ReleaseTemporaryVariable(args[n]->type, 0); + + asCDataType dt = args[n]->type.dataType; + dt.MakeReference(false); + + int l = int(reservedVariables.GetLength()); + objBC.GetVarsUsed(reservedVariables); + ctx->bc.GetVarsUsed(reservedVariables); + int newOffset = AllocateVariable(dt, true, IsVariableOnHeap(args[n]->type.stackOffset)); + reservedVariables.SetLength(l); + + asASSERT( IsVariableOnHeap(args[n]->type.stackOffset) == IsVariableOnHeap(newOffset) ); + + ctx->bc.ExchangeVar(args[n]->type.stackOffset, newOffset); + args[n]->type.stackOffset = (short)newOffset; + args[n]->type.isTemporary = true; + args[n]->type.isVariable = true; + } + } + + // If the function will return a value type on the stack, then we must allocate space + // for that here and push the address on the stack as a hidden argument to the function + asCScriptFunction *func = builder->GetFunctionDescription(funcId); + if( func->DoesReturnOnStack() ) + { + asASSERT(!useVariable); + + useVariable = true; + stackOffset = AllocateVariable(func->returnType, true); + ctx->bc.InstrSHORT(asBC_PSF, short(stackOffset)); + } + + ctx->bc.AddCode(&objBC); + + MoveArgsToStack(funcId, &ctx->bc, args, objectType ? true : false); + + PerformFunctionCall(funcId, ctx, false, &args, 0, useVariable, stackOffset, funcPtrVar); +} + +int asCCompiler::CompileOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op, bool leftToRight) +{ + // Don't allow any operators on expressions that take address of class method, but allow it on global functions + if( (lctx->IsClassMethod()) || (rctx->IsClassMethod()) ) + { + Error(TXT_INVALID_OP_ON_METHOD, node); + return -1; + } + + // Don't allow any operators on void expressions + if( lctx->IsVoidExpression() || rctx->IsVoidExpression() ) + { + Error(TXT_VOID_CANT_BE_OPERAND, node); + return -1; + } + + if( op == ttUnrecognizedToken ) + op = node->tokenType; + + IsVariableInitialized(&lctx->type, node); + IsVariableInitialized(&rctx->type, node); + + if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle || + lctx->type.IsNullConstant() || rctx->type.IsNullConstant() || + op == ttIs || op == ttNotIs ) + { + CompileOperatorOnHandles(node, lctx, rctx, ctx, op); + return 0; + } + else + { + // Compile an overloaded operator for the two operands + if( CompileOverloadedDualOperator(node, lctx, rctx, leftToRight, ctx, false, op) ) + return 0; + + // If both operands are objects, then we shouldn't continue + if( lctx->type.dataType.IsObject() && rctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), rctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return -1; + } + + // Process the property get accessors (if any) + ProcessPropertyGetAccessor(lctx, node); + ProcessPropertyGetAccessor(rctx, node); + + // Make sure we have two variables or constants + if( lctx->type.dataType.IsReference() ) ConvertToVariableNotIn(lctx, rctx); + if( rctx->type.dataType.IsReference() ) ConvertToVariableNotIn(rctx, lctx); + + // Make sure lctx doesn't end up with a variable used in rctx + if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) ) + { + int offset = AllocateVariableNotIn(lctx->type.dataType, true, false, rctx); + rctx->bc.ExchangeVar(lctx->type.stackOffset, offset); + ReleaseTemporaryVariable(offset, 0); + } + + // Math operators + // + - * / % ** += -= *= /= %= **= + if( op == ttPlus || op == ttAddAssign || + op == ttMinus || op == ttSubAssign || + op == ttStar || op == ttMulAssign || + op == ttSlash || op == ttDivAssign || + op == ttPercent || op == ttModAssign || + op == ttStarStar || op == ttPowAssign ) + { + CompileMathOperator(node, lctx, rctx, ctx, op); + return 0; + } + + // Bitwise operators + // << >> >>> & | ^ <<= >>= >>>= &= |= ^= + if( op == ttAmp || op == ttAndAssign || + op == ttBitOr || op == ttOrAssign || + op == ttBitXor || op == ttXorAssign || + op == ttBitShiftLeft || op == ttShiftLeftAssign || + op == ttBitShiftRight || op == ttShiftRightLAssign || + op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + { + CompileBitwiseOperator(node, lctx, rctx, ctx, op); + return 0; + } + + // Comparison operators + // == != < > <= >= + if( op == ttEqual || op == ttNotEqual || + op == ttLessThan || op == ttLessThanOrEqual || + op == ttGreaterThan || op == ttGreaterThanOrEqual ) + { + CompileComparisonOperator(node, lctx, rctx, ctx, op); + return 0; + } + + // Boolean operators + // && || ^^ + if( op == ttAnd || op == ttOr || op == ttXor ) + { + CompileBooleanOperator(node, lctx, rctx, ctx, op); + return 0; + } + } + + asASSERT(false); + return -1; +} + +void asCCompiler::ConvertToTempVariableNotIn(asCExprContext *ctx, asCExprContext *exclude) +{ + int l = int(reservedVariables.GetLength()); + if( exclude ) exclude->bc.GetVarsUsed(reservedVariables); + ConvertToTempVariable(ctx); + reservedVariables.SetLength(l); +} + +void asCCompiler::ConvertToTempVariable(asCExprContext *ctx) +{ + // This is only used for primitive types and null handles + asASSERT( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsNullHandle() ); + + ConvertToVariable(ctx); + if( !ctx->type.isTemporary ) + { + if( ctx->type.dataType.IsPrimitive() ) + { + // Copy the variable to a temporary variable + int offset = AllocateVariable(ctx->type.dataType, true); + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrW_W(asBC_CpyVtoV4, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_CpyVtoV8, offset, ctx->type.stackOffset); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + } + else + { + // We should never get here + asASSERT(false); + } + } +} + +void asCCompiler::ConvertToVariable(asCExprContext *ctx) +{ + // We should never get here while the context is still an unprocessed property accessor + asASSERT(ctx->property_get == 0 && ctx->property_set == 0); + + int offset; + if( !ctx->type.isVariable && + (ctx->type.dataType.IsObjectHandle() || + (ctx->type.dataType.IsObject() && ctx->type.dataType.SupportHandles())) ) + { + offset = AllocateVariable(ctx->type.dataType, true); + if( ctx->type.IsNullConstant() ) + { + if( ctx->bc.GetLastInstr() == asBC_PshNull ) + ctx->bc.Instr(asBC_PopPtr); // Pop the null constant pushed onto the stack + ctx->bc.InstrSHORT(asBC_ClrVPtr, (short)offset); + } + else + { + Dereference(ctx, true); + + // Copy the object handle to a variable + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( ctx->type.dataType.IsFuncdef() ) + ctx->bc.InstrPTR(asBC_REFCPY, &engine->functionBehaviours); + else + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo()); + ctx->bc.Instr(asBC_PopPtr); + } + + // As this is an object the reference must be placed on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + ctx->type.dataType.MakeHandle(true); + ctx->type.dataType.MakeReference(true); + } + else if( (!ctx->type.isVariable || ctx->type.dataType.IsReference()) && + ctx->type.dataType.IsPrimitive() ) + { + if( ctx->type.isConstant ) + { + offset = AllocateVariable(ctx->type.dataType, true); + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, ctx->type.GetConstantB()); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + ctx->bc.InstrSHORT_W(asBC_SetV2, (short)offset, ctx->type.GetConstantW()); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, ctx->type.GetConstantDW()); + else + ctx->bc.InstrSHORT_QW(asBC_SetV8, (short)offset, ctx->type.GetConstantQW()); + + ctx->type.SetVariable(ctx->type.dataType, offset, true); + return; + } + else + { + asASSERT(ctx->type.dataType.IsPrimitive()); + asASSERT(ctx->type.dataType.IsReference()); + + ctx->type.dataType.MakeReference(false); + offset = AllocateVariable(ctx->type.dataType, true); + + // Read the value from the address in the register directly into the variable + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + ctx->bc.InstrSHORT(asBC_RDR1, (short)offset); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + ctx->bc.InstrSHORT(asBC_RDR2, (short)offset); + else if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_RDR4, (short)offset); + else + ctx->bc.InstrSHORT(asBC_RDR8, (short)offset); + } + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + } +} + +void asCCompiler::ConvertToVariableNotIn(asCExprContext *ctx, asCExprContext *exclude) +{ + int l = int(reservedVariables.GetLength()); + if( exclude ) exclude->bc.GetVarsUsed(reservedVariables); + ConvertToVariable(ctx); + reservedVariables.SetLength(l); +} + +void asCCompiler::ImplicitConvObjectToBestMathType(asCExprContext *ctx, asCScriptNode *node) +{ + asCArray funcs; + asCObjectType *ot = CastToObjectType(ctx->type.dataType.GetTypeInfo()); + if( ot ) + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + // Consider only implicit casts + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == "opImplConv" && + func->returnType.IsPrimitive() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); + } + + // Use the one with the highest precision + const eTokenType match[10] = {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}; + while( funcs.GetLength() > 1 ) + { + eTokenType returnType = builder->GetFunctionDescription(funcs[0])->returnType.GetTokenType(); + int value1 = 11, value2 = 11; + for( asUINT i = 0; i < 10; i++ ) + { + if( returnType == match[i] ) + { + value1 = i; + break; + } + } + + for( asUINT n = 1; n < funcs.GetLength(); n++ ) + { + returnType = builder->GetFunctionDescription(funcs[n])->returnType.GetTokenType(); + for( asUINT i = 0; i < 10; i++ ) + { + if( returnType == match[i] ) + { + value2 = i; + break; + } + } + + if( value2 >= value1 ) + { + // Remove this and continue searching + funcs.RemoveIndexUnordered(n--); + } + else + { + // Remove the first, and start over + funcs.RemoveIndexUnordered(0); + break; + } + } + } + + // Do the conversion + if( funcs.GetLength() ) + ImplicitConvObjectToPrimitive(ctx, builder->GetFunctionDescription(funcs[0])->returnType, node, asIC_IMPLICIT_CONV); + } +} + +void asCCompiler::CompileMathOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op) +{ + // TODO: If a constant is only using 32bits, then a 32bit operation is preferred + + // TODO: clean up: This initial part is identical to CompileComparisonOperator. Make a common function out of it + + // If either operand is a non-primitive then use the primitive type + if( !lctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(lctx, node); + reservedVariables.SetLength(l); + } + if( !rctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(rctx, node); + reservedVariables.SetLength(l); + } + + // Both types must now be primitives. Implicitly convert them so they match + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + to.SetTokenType(ttDouble); + else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) + to.SetTokenType(ttFloat); + else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // Convert to int64 if both are signed or if one is non-constant and signed + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) + to.SetTokenType(ttInt64); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt64); + else + to.SetTokenType(ttInt64); + } + else + { + // Convert to int32 if both are signed or if one is non-constant and signed + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) + to.SetTokenType(ttInt); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + else + to.SetTokenType(ttInt); + } + + // If doing an operation with double constant and float variable, the constant should be converted to float + if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) || + (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) ) + to.SetTokenType(ttFloat); + + if( op == ttUnrecognizedToken ) + op = node->tokenType; + + // If integer division is disabled, convert to floating-point + if( engine->ep.disableIntegerDivision && + (op == ttSlash || op == ttDivAssign) && + (to.IsIntegerType() || to.IsUnsignedType()) ) + { + // Use double to avoid losing precision when dividing with 32bit ints + // For 64bit ints there is unfortunately no greater type so with those + // there is still a risk of loosing precision + to.SetTokenType(ttDouble); + } + + // Do the actual conversion + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + lctx->bc.GetVarsUsed(reservedVariables); + + if( lctx->type.dataType.IsReference() ) + ConvertToVariable(lctx); + if( rctx->type.dataType.IsReference() ) + ConvertToVariable(rctx); + + if( to.IsPrimitive() ) + { + // ttStarStar allows an integer, right-hand operand and a double + // left-hand operand. + if( (op == ttStarStar || op == ttPowAssign) && + lctx->type.dataType.IsDoubleType() && + (rctx->type.dataType.IsIntegerType() || + rctx->type.dataType.IsUnsignedType()) ) + { + to.SetTokenType(ttInt); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true); + to.SetTokenType(ttDouble); + } + else + { + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true); + } + } + reservedVariables.SetLength(l); + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsIntegerType() && + !lctx->type.dataType.IsUnsignedType() && + !lctx->type.dataType.IsFloatType() && + !lctx->type.dataType.IsDoubleType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.SetDummy(); + return; + } + + if( !rctx->type.dataType.IsIntegerType() && + !rctx->type.dataType.IsUnsignedType() && + !rctx->type.dataType.IsFloatType() && + !rctx->type.dataType.IsDoubleType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + ctx->type.SetDummy(); + return; + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + // Verify if we are dividing with a constant zero + if( rctx->type.isConstant && + (op == ttSlash || op == ttDivAssign || + op == ttPercent || op == ttModAssign) && + ((rctx->type.dataType.GetSizeInMemoryBytes() == 4 && rctx->type.GetConstantDW() == 0) || + (rctx->type.dataType.GetSizeInMemoryBytes() == 8 && rctx->type.GetConstantQW() == 0) || + (rctx->type.dataType.GetSizeInMemoryBytes() == 1 && rctx->type.GetConstantB() == 0) || + (rctx->type.dataType.GetSizeInMemoryBytes() == 2 && rctx->type.GetConstantW() == 0)) ) + { + Error(TXT_DIVIDE_BY_ZERO, node); + } + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttAddAssign || op == ttSubAssign || + op == ttMulAssign || op == ttDivAssign || + op == ttModAssign || op == ttPowAssign ) + { + // Merge the operands in the different order so that they are evaluated correctly + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + + // We must not process the deferred parameters yet, as + // it may overwrite the lvalue kept in the register + } + else + { + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + + ProcessDeferredParams(ctx); + } + + asEBCInstr instruction = asBC_ADDi; + if( lctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsUnsignedType() ) + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDi; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBi; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULi; + else if( op == ttSlash || op == ttDivAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_DIVi; + else + instruction = asBC_DIVu; + } + else if( op == ttPercent || op == ttModAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_MODi; + else + instruction = asBC_MODu; + } + else if( op == ttStarStar || op == ttPowAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_POWi; + else + instruction = asBC_POWu; + } + } + else + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDi64; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBi64; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULi64; + else if( op == ttSlash || op == ttDivAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_DIVi64; + else + instruction = asBC_DIVu64; + } + else if( op == ttPercent || op == ttModAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_MODi64; + else + instruction = asBC_MODu64; + } + else if( op == ttStarStar || op == ttPowAssign ) + { + if( lctx->type.dataType.IsIntegerType() ) + instruction = asBC_POWi64; + else + instruction = asBC_POWu64; + } + } + } + else if( lctx->type.dataType.IsFloatType() ) + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDf; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBf; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULf; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVf; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODf; + else if( op == ttStarStar || op == ttPowAssign ) + instruction = asBC_POWf; + } + else if( lctx->type.dataType.IsDoubleType() ) + { + if( rctx->type.dataType.IsIntegerType() ) + { + asASSERT(rctx->type.dataType.GetSizeInMemoryDWords() == 1); + + if( op == ttStarStar || op == ttPowAssign ) + instruction = asBC_POWdi; + else + asASSERT(false); // Should not be possible + } + else + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDd; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBd; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULd; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVd; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODd; + else if( op == ttStarStar || op == ttPowAssign ) + instruction = asBC_POWd; + } + } + else + { + // Shouldn't be possible + asASSERT(false); + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + // Both values are constants + if( lctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsUnsignedType() ) + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + int v = 0; + if( op == ttPlus ) + v = int(lctx->type.GetConstantDW()) + int(rctx->type.GetConstantDW()); + else if( op == ttMinus ) + v = int(lctx->type.GetConstantDW()) - int(rctx->type.GetConstantDW()); + else if( op == ttStar ) + v = int(lctx->type.GetConstantDW()) * int(rctx->type.GetConstantDW()); + else if( op == ttSlash ) + { + // TODO: Should probably report an error, rather than silently convert the value to 0 + if( rctx->type.GetConstantDW() == 0 || (int(rctx->type.GetConstantDW()) == -1 && lctx->type.GetConstantDW() == 0x80000000) ) + v = 0; + else + if( lctx->type.dataType.IsIntegerType() ) + v = int(lctx->type.GetConstantDW()) / int(rctx->type.GetConstantDW()); + else + v = lctx->type.GetConstantDW() / rctx->type.GetConstantDW(); + } + else if( op == ttPercent ) + { + // TODO: Should probably report an error, rather than silently convert the value to 0 + if( rctx->type.GetConstantDW() == 0 || (int(rctx->type.GetConstantDW()) == -1 && lctx->type.GetConstantDW() == 0x80000000) ) + v = 0; + else + if( lctx->type.dataType.IsIntegerType() ) + v = int(lctx->type.GetConstantDW()) % int(rctx->type.GetConstantDW()); + else + v = lctx->type.GetConstantDW() % rctx->type.GetConstantDW(); + } + else if( op == ttStarStar ) + { + bool isOverflow; + if( lctx->type.dataType.IsIntegerType() ) + v = as_powi(int(lctx->type.GetConstantDW()), int(rctx->type.GetConstantDW()), isOverflow); + else + v = as_powu(lctx->type.GetConstantDW(), rctx->type.GetConstantDW(), isOverflow); + + if( isOverflow ) + Error(TXT_POW_OVERFLOW, node); + } + + ctx->type.SetConstantDW(lctx->type.dataType, v); + + // If the right value is greater than the left value in a minus operation, then we need to convert the type to int + if( lctx->type.dataType.GetTokenType() == ttUInt && op == ttMinus && lctx->type.GetConstantDW() < rctx->type.GetConstantDW()) + ctx->type.dataType.SetTokenType(ttInt); + } + else + { + asQWORD v = 0; + if( op == ttPlus ) + v = asINT64(lctx->type.GetConstantQW()) + asINT64(rctx->type.GetConstantQW()); + else if( op == ttMinus ) + v = asINT64(lctx->type.GetConstantQW()) - asINT64(rctx->type.GetConstantQW()); + else if( op == ttStar ) + v = asINT64(lctx->type.GetConstantQW()) * asINT64(rctx->type.GetConstantQW()); + else if( op == ttSlash ) + { + // TODO: Should probably report an error, rather than silently convert the value to 0 + if( rctx->type.GetConstantQW() == 0 || (rctx->type.GetConstantQW() == asQWORD(-1) && lctx->type.GetConstantQW() == (asQWORD(1)<<63)) ) + v = 0; + else + if( lctx->type.dataType.IsIntegerType() ) + v = asINT64(lctx->type.GetConstantQW()) / asINT64(rctx->type.GetConstantQW()); + else + v = lctx->type.GetConstantQW() / rctx->type.GetConstantQW(); + } + else if( op == ttPercent ) + { + // TODO: Should probably report an error, rather than silently convert the value to 0 + if( rctx->type.GetConstantQW() == 0 || (rctx->type.GetConstantQW() == asQWORD(-1) && lctx->type.GetConstantQW() == (asQWORD(1)<<63)) ) + v = 0; + else + if( lctx->type.dataType.IsIntegerType() ) + v = asINT64(lctx->type.GetConstantQW()) % asINT64(rctx->type.GetConstantQW()); + else + v = lctx->type.GetConstantQW() % rctx->type.GetConstantQW(); + } + else if( op == ttStarStar ) + { + bool isOverflow; + if( lctx->type.dataType.IsIntegerType() ) + v = as_powi64(asINT64(lctx->type.GetConstantQW()), asINT64(rctx->type.GetConstantQW()), isOverflow); + else + v = as_powu64(lctx->type.GetConstantQW(), rctx->type.GetConstantQW(), isOverflow); + + if( isOverflow ) + Error(TXT_POW_OVERFLOW, node); + } + + ctx->type.SetConstantQW(lctx->type.dataType, v); + + // If the right value is greater than the left value in a minus operation, then we need to convert the type to int + if( lctx->type.dataType.GetTokenType() == ttUInt64 && op == ttMinus && lctx->type.GetConstantQW() < rctx->type.GetConstantQW()) + ctx->type.dataType.SetTokenType(ttInt64); + } + } + else if( lctx->type.dataType.IsFloatType() ) + { + float v = 0.0f; + if( op == ttPlus ) + v = lctx->type.GetConstantF() + rctx->type.GetConstantF(); + else if( op == ttMinus ) + v = lctx->type.GetConstantF() - rctx->type.GetConstantF(); + else if( op == ttStar ) + v = lctx->type.GetConstantF() * rctx->type.GetConstantF(); + else if( op == ttSlash ) + { + if( rctx->type.GetConstantF() == 0 ) + v = 0; + else + v = lctx->type.GetConstantF() / rctx->type.GetConstantF(); + } + else if( op == ttPercent ) + { + if( rctx->type.GetConstantF() == 0 ) + v = 0; + else + v = fmodf(lctx->type.GetConstantF(), rctx->type.GetConstantF()); + } + else if( op == ttStarStar ) + { + v = powf(lctx->type.GetConstantF(), rctx->type.GetConstantF()); + + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } + + ctx->type.SetConstantF(lctx->type.dataType, v); + } + else if( lctx->type.dataType.IsDoubleType() ) + { + double v = 0.0; + if( rctx->type.dataType.IsIntegerType() ) + { + asASSERT(rctx->type.dataType.GetSizeInMemoryDWords() == 1); + + if( op == ttStarStar || op == ttPowAssign ) + { + v = pow(lctx->type.GetConstantD(), int(rctx->type.GetConstantDW())); + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } + else + asASSERT(false); // Should not be possible + } + else + { + if( op == ttPlus ) + v = lctx->type.GetConstantD() + rctx->type.GetConstantD(); + else if( op == ttMinus ) + v = lctx->type.GetConstantD() - rctx->type.GetConstantD(); + else if( op == ttStar ) + v = lctx->type.GetConstantD() * rctx->type.GetConstantD(); + else if( op == ttSlash ) + { + if( rctx->type.GetConstantD() == 0 ) + v = 0; + else + v = lctx->type.GetConstantD() / rctx->type.GetConstantD(); + } + else if( op == ttPercent ) + { + if( rctx->type.GetConstantD() == 0 ) + v = 0; + else + v = fmod(lctx->type.GetConstantD(), rctx->type.GetConstantD()); + } + else if( op == ttStarStar ) + { + v = pow(lctx->type.GetConstantD(), rctx->type.GetConstantD()); + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } + } + + ctx->type.SetConstantD(lctx->type.dataType, v); + } + else + { + // Shouldn't be possible + asASSERT(false); + } + } +} + +void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op) +{ + // TODO: If a constant is only using 32bits, then a 32bit operation is preferred + + if( op == ttUnrecognizedToken ) + op = node->tokenType; + if( op == ttAmp || op == ttAndAssign || + op == ttBitOr || op == ttOrAssign || + op == ttBitXor || op == ttXorAssign ) + { + // Convert left hand operand to integer if it's not already one + asCDataType to; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || + rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + to.SetTokenType(ttInt64); + else + to.SetTokenType(ttInt); + + // Do the actual conversion (keep sign/unsigned if possible) + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + if( lctx->type.dataType.IsUnsignedType() ) + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 ); + else + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 ); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true); + reservedVariables.SetLength(l); + + // Verify that the conversion was successful + if( lctx->type.dataType != to ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + // Convert right hand operand to same size as left hand + l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + if( rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 ); + else + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 ); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true); + reservedVariables.SetLength(l); + if( rctx->type.dataType != to ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttAndAssign || op == ttOrAssign || op == ttXorAssign ) + { + // Compound assignments execute the right hand value first + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + } + else + { + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + } + ProcessDeferredParams(ctx); + + asEBCInstr instruction = asBC_BAND; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttAmp || op == ttAndAssign ) + instruction = asBC_BAND; + else if( op == ttBitOr || op == ttOrAssign ) + instruction = asBC_BOR; + else if( op == ttBitXor || op == ttXorAssign ) + instruction = asBC_BXOR; + } + else + { + if( op == ttAmp || op == ttAndAssign ) + instruction = asBC_BAND64; + else if( op == ttBitOr || op == ttOrAssign ) + instruction = asBC_BOR64; + else if( op == ttBitXor || op == ttXorAssign ) + instruction = asBC_BXOR64; + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asQWORD v = 0; + if( op == ttAmp ) + v = lctx->type.GetConstantQW() & rctx->type.GetConstantQW(); + else if( op == ttBitOr ) + v = lctx->type.GetConstantQW() | rctx->type.GetConstantQW(); + else if( op == ttBitXor ) + v = lctx->type.GetConstantQW() ^ rctx->type.GetConstantQW(); + + // Remember the result + ctx->type.SetConstantQW(lctx->type.dataType, v); + } + else + { + asDWORD v = 0; + if( op == ttAmp ) + v = lctx->type.GetConstantDW() & rctx->type.GetConstantDW(); + else if( op == ttBitOr ) + v = lctx->type.GetConstantDW() | rctx->type.GetConstantDW(); + else if( op == ttBitXor ) + v = lctx->type.GetConstantDW() ^ rctx->type.GetConstantDW(); + + // Remember the result + ctx->type.SetConstantDW(lctx->type.dataType, v); + } + } + } + else if( op == ttBitShiftLeft || op == ttShiftLeftAssign || + op == ttBitShiftRight || op == ttShiftRightLAssign || + op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + { + // Don't permit object to primitive conversion, since we don't know which integer type is the correct one + if( lctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + + // Set an integer value and allow the compiler to continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0); + return; + } + + // Convert left hand operand to integer if it's not already one + asCDataType to = lctx->type.dataType; + if( lctx->type.dataType.IsUnsignedType() && + lctx->type.dataType.GetSizeInMemoryBytes() < 4 ) + { + // Upgrade to 32bit + to = asCDataType::CreatePrimitive(ttUInt, false); + } + else if( !lctx->type.dataType.IsUnsignedType() ) + { + if (lctx->type.dataType.GetSizeInMemoryDWords() == 2) + to = asCDataType::CreatePrimitive(ttInt64, false); + else + to = asCDataType::CreatePrimitive(ttInt, false); + } + + // Do the actual conversion + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true); + reservedVariables.SetLength(l); + + // Verify that the conversion was successful + if( lctx->type.dataType != to ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + // Right operand must be 32bit uint + l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + ImplicitConversion(rctx, asCDataType::CreatePrimitive(ttUInt, true), node, asIC_IMPLICIT_CONV, true); + reservedVariables.SetLength(l); + if( !rctx->type.dataType.IsUnsignedType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "uint"); + Error(str, node); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttShiftLeftAssign || op == ttShiftRightLAssign || op == ttShiftRightAAssign ) + { + // Compound assignments execute the right hand value first + MergeExprBytecode(ctx, rctx); + MergeExprBytecode(ctx, lctx); + } + else + { + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + } + ProcessDeferredParams(ctx); + + asEBCInstr instruction = asBC_BSLL; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttBitShiftLeft || op == ttShiftLeftAssign ) + instruction = asBC_BSLL; + else if( op == ttBitShiftRight || op == ttShiftRightLAssign ) + instruction = asBC_BSRL; + else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + instruction = asBC_BSRA; + } + else + { + if( op == ttBitShiftLeft || op == ttShiftLeftAssign ) + instruction = asBC_BSLL64; + else if( op == ttBitShiftRight || op == ttShiftRightLAssign ) + instruction = asBC_BSRL64; + else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + instruction = asBC_BSRA64; + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + asDWORD v = 0; + if( op == ttBitShiftLeft ) + v = lctx->type.GetConstantDW() << rctx->type.GetConstantDW(); + else if( op == ttBitShiftRight ) + v = lctx->type.GetConstantDW() >> rctx->type.GetConstantDW(); + else if( op == ttBitShiftRightArith ) + v = int(lctx->type.GetConstantDW()) >> rctx->type.GetConstantDW(); + + ctx->type.SetConstantDW(lctx->type.dataType, v); + } + else + { + asQWORD v = 0; + if( op == ttBitShiftLeft ) + v = lctx->type.GetConstantQW() << rctx->type.GetConstantDW(); + else if( op == ttBitShiftRight ) + v = lctx->type.GetConstantQW() >> rctx->type.GetConstantDW(); + else if( op == ttBitShiftRightArith ) + v = asINT64(lctx->type.GetConstantQW()) >> rctx->type.GetConstantDW(); + + ctx->type.SetConstantQW(lctx->type.dataType, v); + } + } + } +} + +void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op) +{ + // Both operands must be of the same type + + // If either operand is a non-primitive then first convert them to the best number type + if( !lctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(lctx, node); + reservedVariables.SetLength(l); + } + if( !rctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(rctx, node); + reservedVariables.SetLength(l); + } + + // Implicitly convert the operands to matching types + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + to.SetTokenType(ttDouble); + else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) + to.SetTokenType(ttFloat); + else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // Convert to int64 if both are signed or if one is non-constant and signed + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) + to.SetTokenType(ttInt64); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt64); + else + to.SetTokenType(ttInt64); + } + else + { + // Convert to int32 if both are signed or if one is non-constant and signed + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) + to.SetTokenType(ttInt); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() ) + to.SetTokenType(ttBool); + else + to.SetTokenType(ttInt); + } + + // If doing an operation with double constant and float variable, the constant should be converted to float + if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) || + (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) ) + to.SetTokenType(ttFloat); + + asASSERT( to.GetTokenType() != ttUnrecognizedToken ); + + // Do we have a mismatch between the sign of the operand? + bool signMismatch = false; + for( int n = 0; !signMismatch && n < 2; n++ ) + { + asCExprContext *opCtx = n ? rctx : lctx; + + if( opCtx->type.dataType.IsUnsignedType() != to.IsUnsignedType() ) + { + // We have a mismatch, unless the value is a literal constant and the conversion won't affect its value + signMismatch = true; + if( opCtx->type.isConstant ) + { + if( opCtx->type.dataType.GetTokenType() == ttUInt64 || opCtx->type.dataType.GetTokenType() == ttInt64 ) + { + if( !(opCtx->type.GetConstantQW() & (asQWORD(1)<<63)) ) + signMismatch = false; + } + else + { + if( !(opCtx->type.GetConstantDW() & (1<<31)) ) + signMismatch = false; + } + + // It's not necessary to check for floats or double, because if + // it was then the types for the conversion will never be unsigned + } + } + } + + // Check for signed/unsigned mismatch + if( signMismatch ) + Warning(TXT_SIGNED_UNSIGNED_MISMATCH, node); + + // Attempt to resolve ambiguous enumerations + if( lctx->type.dataType.IsEnumType() && rctx->enumValue != "" ) + ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV); + else if( rctx->type.dataType.IsEnumType() && lctx->enumValue != "" ) + ImplicitConversion(lctx, rctx->type.dataType, node, asIC_IMPLICIT_CONV); + + // Do the actual conversion + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + + if( lctx->type.dataType.IsReference() ) + ConvertToVariable(lctx); + if( rctx->type.dataType.IsReference() ) + ConvertToVariable(rctx); + + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + reservedVariables.SetLength(l); + + // Verify that the conversion was successful + bool ok = true; + if( !lctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ok = false; + } + + if( !rctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ok = false; + } + + if( !ok ) + { + // It wasn't possible to get two valid operands, so we just return + // a boolean result and let the compiler continue. +#if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); +#else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); +#endif + return; + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + if( op == ttUnrecognizedToken ) + op = node->tokenType; + + if( !isConstant ) + { + if( to.IsBooleanType() ) + { + if( op == ttEqual || op == ttNotEqual ) + { + // Must convert to temporary variable, because we are changing the value before comparison + ConvertToTempVariableNotIn(lctx, rctx); + ConvertToTempVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + // Make sure they are equal if not false + lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset); + rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset); + + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + ProcessDeferredParams(ctx); + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + if( op == ttEqual ) + { + ctx->bc.InstrW_W(asBC_CMPi,b,c); + ctx->bc.Instr(asBC_TZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + else if( op == ttNotEqual ) + { + ctx->bc.InstrW_W(asBC_CMPi,b,c); + ctx->bc.Instr(asBC_TNZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); +#if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), 0); +#else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 0); +#endif + } + } + else + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + ProcessDeferredParams(ctx); + + asEBCInstr iCmp = asBC_CMPi, iT = asBC_TZ; + + if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + iCmp = asBC_CMPi; + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + iCmp = asBC_CMPu; + else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + iCmp = asBC_CMPi64; + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + iCmp = asBC_CMPu64; + else if( lctx->type.dataType.IsFloatType() ) + iCmp = asBC_CMPf; + else if( lctx->type.dataType.IsDoubleType() ) + iCmp = asBC_CMPd; + else + asASSERT(false); + + if( op == ttEqual ) + iT = asBC_TZ; + else if( op == ttNotEqual ) + iT = asBC_TNZ; + else if( op == ttLessThan ) + iT = asBC_TS; + else if( op == ttLessThanOrEqual ) + iT = asBC_TNP; + else if( op == ttGreaterThan ) + iT = asBC_TP; + else if( op == ttGreaterThanOrEqual ) + iT = asBC_TNS; + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W(iCmp, b, c); + ctx->bc.Instr(iT); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + } + else + { + if( to.IsBooleanType() ) + { + if( op == ttEqual || op == ttNotEqual ) + { + asDWORD lv, rv; + #if AS_SIZEOF_BOOL == 1 + lv = lctx->type.GetConstantB(); + rv = rctx->type.GetConstantB(); + #else + lv = lctx->type.GetConstantDW(); + rv = rctx->type.GetConstantDW(); + #endif + + // Make sure they are equal if not false + if (lv != 0) lv = VALUE_OF_BOOLEAN_TRUE; + if (rv != 0) rv = VALUE_OF_BOOLEAN_TRUE; + + asDWORD v = 0; + if (op == ttEqual) + v = (lv == rv) ? VALUE_OF_BOOLEAN_TRUE : 0; + else if (op == ttNotEqual) + v = (lv != rv) ? VALUE_OF_BOOLEAN_TRUE : 0; + + #if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), (asBYTE)v); + #else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), v); + #endif + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); + } + } + else + { + int i = 0; + if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + int v = int(lctx->type.GetConstantDW()) - int(rctx->type.GetConstantDW()); + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + asDWORD v1 = lctx->type.GetConstantDW(); + asDWORD v2 = rctx->type.GetConstantDW(); + if( v1 < v2 ) i = -1; + if( v1 > v2 ) i = 1; + } + else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asINT64 v = asINT64(lctx->type.GetConstantQW()) - asINT64(rctx->type.GetConstantQW()); + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asQWORD v1 = lctx->type.GetConstantQW(); + asQWORD v2 = rctx->type.GetConstantQW(); + if( v1 < v2 ) i = -1; + if( v1 > v2 ) i = 1; + } + else if( lctx->type.dataType.IsFloatType() ) + { + float v = lctx->type.GetConstantF() - rctx->type.GetConstantF(); + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsDoubleType() ) + { + double v = lctx->type.GetConstantD() - rctx->type.GetConstantD(); + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + + + if( op == ttEqual ) + i = (i == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttNotEqual ) + i = (i != 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttLessThan ) + i = (i < 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttLessThanOrEqual ) + i = (i <= 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttGreaterThan ) + i = (i > 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttGreaterThanOrEqual ) + i = (i >= 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + + #if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), (asBYTE)i); + #else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), i); + #endif + } + } +} + +void asCCompiler::PushVariableOnStack(asCExprContext *ctx, bool asReference) +{ + // Put the result on the stack + if( asReference ) + { + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + ctx->type.dataType.MakeReference(true); + } + else + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_PshV4, ctx->type.stackOffset); + else + ctx->bc.InstrSHORT(asBC_PshV8, ctx->type.stackOffset); + } +} + +void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType op) +{ + // Both operands must be booleans + asCDataType to; + to.SetTokenType(ttBool); + + // Do the actual conversion + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + lctx->bc.GetVarsUsed(reservedVariables); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); + if( rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + reservedVariables.SetLength(l); + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsBooleanType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool"); + Error(str, node); + // Force the conversion to allow compilation to proceed + lctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + + if( !rctx->type.dataType.IsBooleanType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool"); + Error(str, node); + // Force the conversion to allow compilation to proceed + rctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); + + // What kind of operator is it? + if( op == ttUnrecognizedToken ) + op = node->tokenType; + if( op == ttXor ) + { + if( !isConstant ) + { + // Must convert to temporary variable, because we are changing the value before comparison + ConvertToTempVariableNotIn(lctx, rctx); + ConvertToTempVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + // Make sure they are equal if not false + lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset); + rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset); + + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + ProcessDeferredParams(ctx); + + int a = AllocateVariable(ctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(asBC_BXOR,a,b,c); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + else + { + // Make sure they are equal if not false +#if AS_SIZEOF_BOOL == 1 + if( lctx->type.GetConstantB() != 0 ) lctx->type.SetConstantB(VALUE_OF_BOOLEAN_TRUE); + if( rctx->type.GetConstantB() != 0 ) rctx->type.SetConstantB(VALUE_OF_BOOLEAN_TRUE); + + asBYTE v = 0; + v = lctx->type.GetConstantB() - rctx->type.GetConstantB(); + if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + + ctx->type.isConstant = true; + ctx->type.SetConstantB(v); +#else + if( lctx->type.GetConstantDW() != 0 ) lctx->type.SetConstantDW(VALUE_OF_BOOLEAN_TRUE); + if( rctx->type.GetConstantDW() != 0 ) rctx->type.GetConstantDW(VALUE_OF_BOOLEAN_TRUE); + + asDWORD v = 0; + v = lctx->type.GetConstantDW() - rctx->type.GetConstantDW(); + if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + + ctx->type.isConstant = true; + ctx->type.SetConstantDW(v); +#endif + } + } + else if( op == ttAnd || + op == ttOr ) + { + if( !isConstant ) + { + // If or-operator and first value is 1 the second value shouldn't be calculated + // if and-operator and first value is 0 the second value shouldn't be calculated + ConvertToVariable(lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + MergeExprBytecode(ctx, lctx); + + int offset = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true); + + int label1 = nextLabel++; + int label2 = nextLabel++; + + ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + if( op == ttAnd ) + { + ctx->bc.InstrDWORD(asBC_JNZ, label1); + ctx->bc.InstrW_DW(asBC_SetV4, (asWORD)offset, 0); + ctx->bc.InstrINT(asBC_JMP, label2); + } + else if( op == ttOr ) + { + ctx->bc.InstrDWORD(asBC_JZ, label1); +#if AS_SIZEOF_BOOL == 1 + ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, VALUE_OF_BOOLEAN_TRUE); +#else + ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, VALUE_OF_BOOLEAN_TRUE); +#endif + ctx->bc.InstrINT(asBC_JMP, label2); + } + + ctx->bc.Label((short)label1); + ConvertToVariable(rctx); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + rctx->bc.InstrW_W(asBC_CpyVtoV4, offset, rctx->type.stackOffset); + MergeExprBytecode(ctx, rctx); + ctx->bc.Label((short)label2); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), offset, true); + } + else + { +#if AS_SIZEOF_BOOL == 1 + asBYTE v = 0; + if( op == ttAnd ) + v = lctx->type.GetConstantB() && rctx->type.GetConstantB(); + else if( op == ttOr ) + v = lctx->type.GetConstantB() || rctx->type.GetConstantB(); + + // Remember the result + ctx->type.isConstant = true; + ctx->type.SetConstantB(v); +#else + asDWORD v = 0; + if( op == ttAnd ) + v = lctx->type.GetConstantDW() && rctx->type.GetConstantDW(); + else if( op == ttOr ) + v = lctx->type.GetConstantDW() || rctx->type.GetConstantDW(); + + // Remember the result + ctx->type.isConstant = true; + ctx->type.SetConstantDW(v); +#endif + } + } +} + +void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asCExprContext *lctx, asCExprContext *rctx, asCExprContext *ctx, eTokenType opToken) +{ + // Process the property accessor as get + ProcessPropertyGetAccessor(lctx, node); + ProcessPropertyGetAccessor(rctx, node); + + DetermineSingleFunc(lctx, node); + DetermineSingleFunc(rctx, node); + + // Make sure lctx doesn't end up with a variable used in rctx + if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) ) + { + asCArray vars; + rctx->bc.GetVarsUsed(vars); + int offset = AllocateVariable(lctx->type.dataType, true); + rctx->bc.ExchangeVar(lctx->type.stackOffset, offset); + ReleaseTemporaryVariable(offset, 0); + } + + if( opToken == ttUnrecognizedToken ) + opToken = node->tokenType; + + // Warn if not both operands are explicit handles or null handles + if( (opToken == ttEqual || opToken == ttNotEqual) && + ((!(lctx->type.isExplicitHandle || lctx->type.IsNullConstant()) && !(lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE))) || + (!(rctx->type.isExplicitHandle || rctx->type.IsNullConstant()) && !(rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_IMPLICIT_HANDLE)))) ) + { + Warning(TXT_HANDLE_COMPARISON, node); + } + + // If one of the operands is a value type used as handle, we should look for the opEquals method + if( ((lctx->type.dataType.GetTypeInfo() && (lctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE)) || + (rctx->type.dataType.GetTypeInfo() && (rctx->type.dataType.GetTypeInfo()->flags & asOBJ_ASHANDLE))) && + (opToken == ttEqual || opToken == ttIs || + opToken == ttNotEqual || opToken == ttNotIs) ) + { + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching opEquals method + int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, true, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + if( r == 0 ) + { + // Try again by switching the order of the operands + r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, false, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + } + + if( r == 1 ) + { + if( opToken == ttNotEqual || opToken == ttNotIs ) + ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); + + // Success, don't continue + return; + } + else if( r == 0 ) + { + // Couldn't find opEquals method + Error(TXT_NO_APPROPRIATE_OPEQUALS, node); + } + + // Compiler error, don't continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + return; + } + + + // Implicitly convert null to the other type + asCDataType to; + if( lctx->type.IsNullConstant() ) + to = rctx->type.dataType; + else if( rctx->type.IsNullConstant() ) + to = lctx->type.dataType; + else + { + // Find a common base type + asCExprContext tmp(engine); + tmp.type = rctx->type; + ImplicitConversion(&tmp, lctx->type.dataType, 0, asIC_IMPLICIT_CONV, false); + if( tmp.type.dataType.GetTypeInfo() == lctx->type.dataType.GetTypeInfo() ) + to = lctx->type.dataType; + else + to = rctx->type.dataType; + + // Assume handle-to-const as it is not possible to convert handle-to-const to handle-to-non-const + to.MakeHandleToConst(true); + } + + // Need to pop the value if it is a null constant + if( lctx->type.IsNullConstant() ) + lctx->bc.Instr(asBC_PopPtr); + if( rctx->type.IsNullConstant() ) + rctx->bc.Instr(asBC_PopPtr); + + // Convert both sides to explicit handles + to.MakeHandle(true); + to.MakeReference(false); + + if( !to.IsObjectHandle() ) + { + // Compiler error, don't continue + Error(TXT_OPERANDS_MUST_BE_HANDLES, node); +#if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); +#else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); +#endif + return; + } + + // Do the conversion + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + + // Both operands must be of the same type + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + if( !rctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + + // Make sure it really is handles that are being compared + if( !lctx->type.dataType.IsObjectHandle() ) + { + Error(TXT_OPERANDS_MUST_BE_HANDLES, node); + } + + ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); + + if( opToken == ttEqual || opToken == ttNotEqual || opToken == ttIs || opToken == ttNotIs ) + { + // Make sure handles received as parameters by reference are copied to a local variable before the + // asBC_CmpPtr, so we don't end up comparing the reference to the handle instead of the handle itself + if( lctx->type.isVariable && !lctx->type.isTemporary && lctx->type.stackOffset <= 0 ) + lctx->type.isVariable = false; + if( rctx->type.isVariable && !rctx->type.isTemporary && rctx->type.stackOffset <= 0 ) + rctx->type.isVariable = false; + + // TODO: runtime optimize: don't do REFCPY if not necessary + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariable(rctx); + + // Pop the pointers from the stack as they will not be used + lctx->bc.Instr(asBC_PopPtr); + rctx->bc.Instr(asBC_PopPtr); + + MergeExprBytecode(ctx, lctx); + MergeExprBytecode(ctx, rctx); + + int a = AllocateVariable(ctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W(asBC_CmpPtr, b, c); + + if( opToken == ttEqual || opToken == ttIs ) + ctx->bc.Instr(asBC_TZ); + else if( opToken == ttNotEqual || opToken == ttNotIs ) + ctx->bc.Instr(asBC_TNZ); + + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + + ReleaseTemporaryVariable(lctx->type, &ctx->bc); + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + ProcessDeferredParams(ctx); + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); + } +} + + +void asCCompiler::PerformFunctionCall(int funcId, asCExprContext *ctx, bool isConstructor, asCArray *args, asCObjectType *objType, bool useVariable, int varOffset, int funcPtrVar) +{ + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + + // A shared object may not call non-shared functions + if( outFunc->IsShared() && !descr->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, descr->GetDeclarationStr().AddressOf()); + Error(msg, ctx->exprNode); + } + + // Check if the function is private or protected + if( descr->isPrivate && descr->GetObjectType() != outFunc->GetObjectType() ) + { + asCString msg; + msg.Format(TXT_PRIVATE_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf()); + Error(msg, ctx->exprNode); + } + else if( descr->isProtected && + !(descr->GetObjectType() == outFunc->GetObjectType() || + (outFunc->GetObjectType() && outFunc->GetObjectType()->DerivesFrom(descr->GetObjectType()))) ) + { + asCString msg; + msg.Format(TXT_PROTECTED_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf()); + Error(msg, ctx->exprNode); + } + + int argSize = descr->GetSpaceNeededForArguments(); + + // If we're calling a class method we must make sure the object is guaranteed to stay + // alive throughout the call by holding on to a reference in a local variable. This must + // be done for any methods that return references, and any calls on script objects. + // Application registered objects are assumed to know to keep themselves alive even + // if the method doesn't return a reference. + if( !ctx->type.isHandleSafe && + descr->objectType && + (ctx->type.dataType.IsObjectHandle() || ctx->type.dataType.SupportHandles()) && + (descr->returnType.IsReference() || (ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_SCRIPT_OBJECT)) && + !(ctx->type.isVariable || ctx->type.isTemporary) && + !(ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_SCOPED) && + !(ctx->type.dataType.GetTypeInfo()->GetFlags() & asOBJ_ASHANDLE) ) + { + // TODO: runtime optimize: Avoid this for global variables, by storing a reference to the global variable once in a + // local variable and then refer to the same for each call. An alias for the global variable + // should be stored in the variable scope so that the compiler can find it. For loops and + // scopes that will always be executed, i.e. non-if scopes the alias should be stored in the + // higher scope to increase the probability of re-use. + + int tempRef = AllocateVariable(ctx->type.dataType, true); + ctx->bc.InstrSHORT(asBC_PSF, (short)tempRef); + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetTypeInfo()); + + // Add the release of this reference as a deferred expression + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, tempRef, true); + ctx->deferredParams.PushLast(deferred); + + // Forget the current type + ctx->type.SetDummy(); + } + + // Check if there is a need to add a hidden pointer for when the function returns an object by value + if( descr->DoesReturnOnStack() && !useVariable ) + { + useVariable = true; + varOffset = AllocateVariable(descr->returnType, true); + + // Push the pointer to the pre-allocated space for the return value + ctx->bc.InstrSHORT(asBC_PSF, short(varOffset)); + + if( descr->objectType ) + { + // The object pointer is already on the stack, but should be the top + // one, so we need to swap the pointers in order to get the correct + ctx->bc.Instr(asBC_SwapPtr); + } + } + + if( isConstructor ) + { + // Sometimes the value types are allocated on the heap, + // which is when this way of constructing them is used. + + asASSERT(useVariable == false); + + if( (objType->flags & asOBJ_TEMPLATE) ) + { + asASSERT( descr->funcType == asFUNC_SCRIPT ); + + // Find the id of the real constructor and not the generated stub + asUINT id = 0; + asDWORD *bc = descr->scriptData->byteCode.AddressOf(); + while( bc ) + { + if( (*(asBYTE*)bc) == asBC_CALLSYS ) + { + id = asBC_INTARG(bc); + break; + } + bc += asBCTypeSize[asBCInfo[*(asBYTE*)bc].type]; + } + + asASSERT( id ); + + ctx->bc.InstrPTR(asBC_OBJTYPE, objType); + ctx->bc.Alloc(asBC_ALLOC, objType, id, argSize + AS_PTR_SIZE + AS_PTR_SIZE); + } + else + ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE); + + // The instruction has already moved the returned object to the variable + ctx->type.Set(asCDataType::CreatePrimitive(ttVoid, false)); + ctx->type.isLValue = false; + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + + return; + } + else + { + if( descr->objectType ) + argSize += AS_PTR_SIZE; + + // If the function returns an object by value the address of the location + // where the value should be stored is passed as an argument too + if( descr->DoesReturnOnStack() ) + argSize += AS_PTR_SIZE; + + // TODO: runtime optimize: If it is known that a class method cannot be overridden the call + // should be made with asBC_CALL as it is faster. Examples where this + // is known is for example finalled methods where the class doesn't derive + // from any other, or even non-finalled methods but where it is known + // at compile time the true type of the object. The first should be + // quite easy to determine, but the latter will be quite complex and possibly + // not worth it. + if( descr->funcType == asFUNC_IMPORTED ) + ctx->bc.Call(asBC_CALLBND , descr->id, argSize); + // TODO: Maybe we need two different byte codes + else if( descr->funcType == asFUNC_INTERFACE || descr->funcType == asFUNC_VIRTUAL ) + ctx->bc.Call(asBC_CALLINTF, descr->id, argSize); + else if( descr->funcType == asFUNC_SCRIPT ) + ctx->bc.Call(asBC_CALL , descr->id, argSize); + else if( descr->funcType == asFUNC_SYSTEM ) + { + // Check if we can use the faster asBC_Thiscall1 instruction, i.e. one of + // type &obj::func(int) + // type &obj::func(uint) + if( descr->GetObjectType() && descr->returnType.IsReference() && + descr->parameterTypes.GetLength() == 1 && + (descr->parameterTypes[0].IsIntegerType() || descr->parameterTypes[0].IsUnsignedType()) && + descr->parameterTypes[0].GetSizeInMemoryBytes() == 4 && + !descr->parameterTypes[0].IsReference() ) + ctx->bc.Call(asBC_Thiscall1, descr->id, argSize); + else + ctx->bc.Call(asBC_CALLSYS , descr->id, argSize); + } + else if( descr->funcType == asFUNC_FUNCDEF ) + ctx->bc.CallPtr(asBC_CallPtr, funcPtrVar, argSize); + } + + if( (descr->returnType.IsObject() || descr->returnType.IsFuncdef()) && !descr->returnType.IsReference() ) + { + int returnOffset = 0; + + asCExprValue tmpExpr = ctx->type; + + if( descr->DoesReturnOnStack() ) + { + asASSERT( useVariable ); + + // The variable was allocated before the function was called + returnOffset = varOffset; + ctx->type.SetVariable(descr->returnType, returnOffset, true); + + // The variable was initialized by the function, so we need to mark it as initialized here + ctx->bc.ObjInfo(varOffset, asOBJ_INIT); + } + else + { + if( useVariable ) + { + // Use the given variable + returnOffset = varOffset; + ctx->type.SetVariable(descr->returnType, returnOffset, false); + } + else + { + // Allocate a temporary variable for the returned object + // The returned object will actually be allocated on the heap, so + // we must force the allocation of the variable to do the same + returnOffset = AllocateVariable(descr->returnType, true, !descr->returnType.IsObjectHandle()); + ctx->type.SetVariable(descr->returnType, returnOffset, true); + } + + // Move the pointer from the object register to the temporary variable + ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); + } + + ReleaseTemporaryVariable(tmpExpr, &ctx->bc); + + ctx->type.dataType.MakeReference(IsVariableOnHeap(returnOffset)); + ctx->type.isLValue = false; // It is a reference, but not an lvalue + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + + ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + } + else if( descr->returnType.IsReference() ) + { + asASSERT(useVariable == false); + + // We cannot clean up the arguments yet, because the + // reference might be pointing to one of them. + if( args ) + AfterFunctionCall(funcId, *args, ctx, true); + + // Do not process the output parameters yet, because it + // might invalidate the returned reference + + // If the context holds a variable that needs cleanup + // store it as a deferred parameter so it will be cleaned up + // afterwards. + if( ctx->type.isTemporary ) + { + asSDeferredParam defer; + defer.argNode = 0; + defer.argType = ctx->type; + defer.argInOutFlags = asTM_INOUTREF; + defer.origExpr = 0; + ctx->deferredParams.PushLast(defer); + } + + ctx->type.Set(descr->returnType); + if( !descr->returnType.IsPrimitive() ) + { + ctx->bc.Instr(asBC_PshRPtr); + if( descr->returnType.IsObject() && + !descr->returnType.IsObjectHandle() ) + { + // We are getting the pointer to the object + // not a pointer to a object variable + ctx->type.dataType.MakeReference(false); + } + } + + // A returned reference can be used as lvalue + ctx->type.isLValue = true; + } + else + { + asASSERT(useVariable == false); + + asCExprValue tmpExpr = ctx->type; + + if( descr->returnType.GetSizeInMemoryBytes() ) + { + // Allocate a temporary variable to hold the value, but make sure + // the temporary variable isn't used in any of the deferred arguments + int l = int(reservedVariables.GetLength()); + for( asUINT n = 0; args && n < args->GetLength(); n++ ) + { + asCExprContext *expr = (*args)[n]->origExpr; + if( expr ) + expr->bc.GetVarsUsed(reservedVariables); + } + int offset = AllocateVariable(descr->returnType, true); + reservedVariables.SetLength(l); + + ctx->type.SetVariable(descr->returnType, offset, true); + + // Move the value from the return register to the variable + if( descr->returnType.GetSizeOnStackDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)offset); + else if( descr->returnType.GetSizeOnStackDWords() == 2 ) + ctx->bc.InstrSHORT(asBC_CpyRtoV8, (short)offset); + } + else + ctx->type.Set(descr->returnType); + + ReleaseTemporaryVariable(tmpExpr, &ctx->bc); + + ctx->type.isLValue = false; + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + } +} + +// This only merges the bytecode, but doesn't modify the type of the final context +void asCCompiler::MergeExprBytecode(asCExprContext *before, asCExprContext *after) +{ + before->bc.AddCode(&after->bc); + + for( asUINT n = 0; n < after->deferredParams.GetLength(); n++ ) + { + before->deferredParams.PushLast(after->deferredParams[n]); + after->deferredParams[n].origExpr = 0; + } + + after->deferredParams.SetLength(0); +} + +// This merges both bytecode and the type of the final context +void asCCompiler::MergeExprBytecodeAndType(asCExprContext *before, asCExprContext *after) +{ + MergeExprBytecode(before, after); + + before->Merge(after); +} + +void asCCompiler::FilterConst(asCArray &funcs, bool removeConst) +{ + if( funcs.GetLength() == 0 ) return; + + // This is only done for object methods + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[0]); + if( desc->objectType == 0 ) return; + + // Check if there are any non-const matches + asUINT n; + bool foundNonConst = false; + for( n = 0; n < funcs.GetLength(); n++ ) + { + desc = builder->GetFunctionDescription(funcs[n]); + if( desc->isReadOnly != removeConst ) + { + foundNonConst = true; + break; + } + } + + if( foundNonConst ) + { + // Remove all const methods + for( n = 0; n < funcs.GetLength(); n++ ) + { + desc = builder->GetFunctionDescription(funcs[n]); + if( desc->isReadOnly == removeConst ) + { + if( n == funcs.GetLength() - 1 ) + funcs.PopLast(); + else + funcs[n] = funcs.PopLast(); + + n--; + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +asCExprValue::asCExprValue() +{ + isTemporary = false; + stackOffset = 0; + isConstant = false; + isVariable = false; + isExplicitHandle = false; + qwordValue = 0; + isLValue = false; + isRefToLocal = false; + isHandleSafe = false; +} + +void asCExprValue::Set(const asCDataType &dt) +{ + dataType = dt; + + isTemporary = false; + stackOffset = 0; + isConstant = false; + isVariable = false; + isExplicitHandle = false; + qwordValue = 0; + isLValue = false; + isRefToLocal = false; + isHandleSafe = false; +} + +void asCExprValue::SetVariable(const asCDataType &in_dt, int in_stackOffset, bool in_isTemporary) +{ + Set(in_dt); + + this->isVariable = true; + this->isTemporary = in_isTemporary; + this->stackOffset = (short)in_stackOffset; +} + +void asCExprValue::SetConstantQW(const asCDataType &dt, asQWORD value) +{ + Set(dt); + + isConstant = true; + SetConstantQW(value); +} + +void asCExprValue::SetConstantDW(const asCDataType &dt, asDWORD value) +{ + Set(dt); + + isConstant = true; + SetConstantDW(value); +} + +void asCExprValue::SetConstantB(const asCDataType &dt, asBYTE value) +{ + Set(dt); + + isConstant = true; + SetConstantB(value); +} + +void asCExprValue::SetConstantW(const asCDataType &dt, asWORD value) +{ + Set(dt); + + isConstant = true; + SetConstantW(value); +} + +void asCExprValue::SetConstantF(const asCDataType &dt, float value) +{ + Set(dt); + + isConstant = true; + SetConstantF(value); +} + +void asCExprValue::SetConstantD(const asCDataType &dt, double value) +{ + Set(dt); + + isConstant = true; + SetConstantD(value); +} + +void asCExprValue::SetConstantQW(asQWORD value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 8); + qwordValue = value; +} + +void asCExprValue::SetConstantDW(asDWORD value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 4); + dwordValue = value; +} + +void asCExprValue::SetConstantW(asWORD value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 2); + wordValue = value; +} + +void asCExprValue::SetConstantB(asBYTE value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 1); + byteValue = value; +} + +void asCExprValue::SetConstantF(float value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 4); + floatValue = value; +} + +void asCExprValue::SetConstantD(double value) +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 8); + doubleValue = value; +} + +asQWORD asCExprValue::GetConstantQW() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 8); + return qwordValue; +} + +asDWORD asCExprValue::GetConstantDW() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 4); + return dwordValue; +} + +asWORD asCExprValue::GetConstantW() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 2); + return wordValue; +} + +asBYTE asCExprValue::GetConstantB() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 1); + return byteValue; +} + +float asCExprValue::GetConstantF() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 4); + return floatValue; +} + +double asCExprValue::GetConstantD() +{ + asASSERT(dataType.GetSizeInMemoryBytes() == 8); + return doubleValue; +} + +void asCExprValue::SetConstantData(const asCDataType &dt, asQWORD qw) +{ + Set(dt); + + isConstant = true; + + // This code is necessary to guarantee that the code + // works on both big endian and little endian CPUs. + if (dataType.GetSizeInMemoryBytes() == 1) + byteValue = (asBYTE)qw; + if (dataType.GetSizeInMemoryBytes() == 2) + wordValue = (asWORD)qw; + if (dataType.GetSizeInMemoryBytes() == 4) + dwordValue = (asDWORD)qw; + else + qwordValue = qw; +} + +asQWORD asCExprValue::GetConstantData() +{ + asQWORD qw = 0; + // This code is necessary to guarantee that the code + // works on both big endian and little endian CPUs. + if (dataType.GetSizeInMemoryBytes() == 1) + qw = byteValue; + if (dataType.GetSizeInMemoryBytes() == 2) + qw = wordValue; + if (dataType.GetSizeInMemoryBytes() == 4) + qw = dwordValue; + else + qw = qwordValue; + return qw; +} + +void asCExprValue::SetUndefinedFuncHandle(asCScriptEngine *engine) +{ + // This is used for when the expression evaluates to a + // function, but it is not yet known exactly which. The + // owner expression will hold the name of the function + // to determine the exact function when the signature is + // known. + Set(asCDataType::CreateObjectHandle(&engine->functionBehaviours, true)); + isConstant = true; + isExplicitHandle = false; + qwordValue = 1; // Set to a different value than 0 to differentiate from null constant + isLValue = false; +} + +bool asCExprValue::IsUndefinedFuncHandle() const +{ + if (isConstant == false) return false; + if (qwordValue == 0) return false; + if (isLValue) return false; + if (dataType.GetTypeInfo() == 0) return false; + if (dataType.GetTypeInfo()->name != "$func") return false; + if (dataType.IsFuncdef()) return false; + + return true; +} + +void asCExprValue::SetNullConstant() +{ + Set(asCDataType::CreateNullHandle()); + isConstant = true; + isExplicitHandle = false; + qwordValue = 0; + isLValue = false; +} + +bool asCExprValue::IsNullConstant() const +{ + // We can't check the actual object type, because the null constant may have been cast to another type + if (isConstant && dataType.IsObjectHandle() && qwordValue == 0) + return true; + + return false; +} + +void asCExprValue::SetVoid() +{ + Set(asCDataType::CreatePrimitive(ttVoid, false)); + isLValue = false; + isConstant = true; +} + +bool asCExprValue::IsVoid() const +{ + if (dataType.GetTokenType() == ttVoid) + return true; + + return false; +} + +void asCExprValue::SetDummy() +{ + SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +asCExprContext::asCExprContext(asCScriptEngine *engine) : bc(engine) +{ + property_arg = 0; + + Clear(); +} + +asCExprContext::~asCExprContext() +{ + if (property_arg) + asDELETE(property_arg, asCExprContext); +} + +void asCExprContext::Clear() +{ + bc.ClearAll(); + type.Set(asCDataType()); + deferredParams.SetLength(0); + if (property_arg) + asDELETE(property_arg, asCExprContext); + property_arg = 0; + exprNode = 0; + origExpr = 0; + property_get = 0; + property_set = 0; + property_const = false; + property_handle = false; + property_ref = false; + methodName = ""; + enumValue = ""; + isVoidExpression = false; + isCleanArg = false; +} + +bool asCExprContext::IsClassMethod() const +{ + if (type.dataType.GetTypeInfo() == 0) return false; + if (methodName == "") return false; + if (type.dataType.GetTypeInfo() == &type.dataType.GetTypeInfo()->engine->functionBehaviours) return false; + return true; +} + +bool asCExprContext::IsGlobalFunc() const +{ + if (type.dataType.GetTypeInfo() == 0) return false; + if (methodName == "") return false; + if (type.dataType.GetTypeInfo() != &type.dataType.GetTypeInfo()->engine->functionBehaviours) return false; + return true; +} + +void asCExprContext::SetLambda(asCScriptNode *funcDecl) +{ + asASSERT(funcDecl && funcDecl->nodeType == snFunction); + asASSERT(bc.GetLastInstr() == -1); + + Clear(); + type.SetUndefinedFuncHandle(bc.GetEngine()); + exprNode = funcDecl; +} + +bool asCExprContext::IsLambda() const +{ + if (type.IsUndefinedFuncHandle() && exprNode && exprNode->nodeType == snFunction) + return true; + + return false; +} + +void asCExprContext::SetVoidExpression() +{ + Clear(); + type.SetVoid(); + isVoidExpression = true; +} + +bool asCExprContext::IsVoidExpression() const +{ + if (isVoidExpression && type.IsVoid() && exprNode == 0) + return true; + + return false; +} + +void asCExprContext::Merge(asCExprContext *after) +{ + type = after->type; + property_get = after->property_get; + property_set = after->property_set; + property_const = after->property_const; + property_handle = after->property_handle; + property_ref = after->property_ref; + property_arg = after->property_arg; + exprNode = after->exprNode; + methodName = after->methodName; + enumValue = after->enumValue; + isVoidExpression = after->isVoidExpression; + isCleanArg = after->isCleanArg; + + after->property_arg = 0; + + // Do not copy the origExpr member +} + + + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + + + diff --git a/3rdparty/angelscript/src/as_configgroup.cpp b/3rdparty/angelscript/src/as_configgroup.cpp new file mode 100644 index 0000000..a1e8a18 --- /dev/null +++ b/3rdparty/angelscript/src/as_configgroup.cpp @@ -0,0 +1,212 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_configgroup.cpp +// +// This class holds configuration groups for the engine +// + + + +#include "as_config.h" +#include "as_configgroup.h" +#include "as_scriptengine.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +asCConfigGroup::asCConfigGroup() +{ + refCount = 0; +} + +asCConfigGroup::~asCConfigGroup() +{ +} + +int asCConfigGroup::AddRef() +{ + refCount++; + return refCount; +} + +int asCConfigGroup::Release() +{ + // Don't delete the object here, the engine will delete the object when ready + refCount--; + return refCount; +} + +asCTypeInfo *asCConfigGroup::FindType(const char *obj) +{ + for( asUINT n = 0; n < types.GetLength(); n++ ) + if( types[n]->name == obj ) + return types[n]; + + return 0; +} + +void asCConfigGroup::RefConfigGroup(asCConfigGroup *group) +{ + if( group == this || group == 0 ) return; + + // Verify if the group is already referenced + for( asUINT n = 0; n < referencedConfigGroups.GetLength(); n++ ) + if( referencedConfigGroups[n] == group ) + return; + + referencedConfigGroups.PushLast(group); + group->AddRef(); +} + +void asCConfigGroup::AddReferencesForFunc(asCScriptEngine *engine, asCScriptFunction *func) +{ + AddReferencesForType(engine, func->returnType.GetTypeInfo()); + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + AddReferencesForType(engine, func->parameterTypes[n].GetTypeInfo()); +} + +void asCConfigGroup::AddReferencesForType(asCScriptEngine *engine, asCTypeInfo *type) +{ + if( type == 0 ) return; + + // Keep reference to other groups + RefConfigGroup(engine->FindConfigGroupForTypeInfo(type)); + + // Keep track of which generated template instances the config group uses + if( type->flags & asOBJ_TEMPLATE && engine->generatedTemplateTypes.Exists(CastToObjectType(type)) && !generatedTemplateInstances.Exists(CastToObjectType(type)) ) + generatedTemplateInstances.PushLast(CastToObjectType(type)); +} + +bool asCConfigGroup::HasLiveObjects() +{ + for( asUINT n = 0; n < types.GetLength(); n++ ) + if( types[n]->externalRefCount.get() != 0 ) + return true; + + return false; +} + +void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine, bool notUsed) +{ + asASSERT( refCount == 0 ); + + asUINT n; + + // Remove global variables + for( n = 0; n < globalProps.GetLength(); n++ ) + { + int index = engine->registeredGlobalProps.GetIndex(globalProps[n]); + if( index >= 0 ) + { + globalProps[n]->Release(); + + // TODO: global: Should compact the registeredGlobalProps array + engine->registeredGlobalProps.Erase(index); + } + } + globalProps.SetLength(0); + + // Remove global functions + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + int index = engine->registeredGlobalFuncs.GetIndex(scriptFunctions[n]); + if( index >= 0 ) + engine->registeredGlobalFuncs.Erase(index); + scriptFunctions[n]->ReleaseInternal(); + if( engine->stringFactory == scriptFunctions[n] ) + engine->stringFactory = 0; + } + scriptFunctions.SetLength(0); + + // Remove behaviours and members of object types + for( n = 0; n < types.GetLength(); n++ ) + { + asCObjectType *obj = CastToObjectType(types[n]); + if( obj ) + obj->ReleaseAllFunctions(); + } + + // Remove object types (skip this if it is possible other groups are still using the types) + if( !notUsed ) + { + for( n = asUINT(types.GetLength()); n-- > 0; ) + { + asCTypeInfo *t = types[n]; + asSMapNode *cursor; + if( engine->allRegisteredTypes.MoveTo(&cursor, asSNameSpaceNamePair(t->nameSpace, t->name)) && + cursor->value == t ) + { + engine->allRegisteredTypes.Erase(cursor); + + if( engine->defaultArrayObjectType == t ) + engine->defaultArrayObjectType = 0; + + if( t->flags & asOBJ_TYPEDEF ) + engine->registeredTypeDefs.RemoveValue(CastToTypedefType(t)); + else if( t->flags & asOBJ_ENUM ) + engine->registeredEnums.RemoveValue(CastToEnumType(t)); + else if (t->flags & asOBJ_TEMPLATE) + engine->registeredTemplateTypes.RemoveValue(CastToObjectType(t)); + else if (t->flags & asOBJ_FUNCDEF) + { + engine->registeredFuncDefs.RemoveValue(CastToFuncdefType(t)); + engine->RemoveFuncdef(CastToFuncdefType(t)); + } + else + engine->registeredObjTypes.RemoveValue(CastToObjectType(t)); + + t->DestroyInternal(); + t->ReleaseInternal(); + } + else + { + int idx = engine->templateInstanceTypes.IndexOf(CastToObjectType(t)); + if( idx >= 0 ) + { + engine->templateInstanceTypes.RemoveIndexUnordered(idx); + asCObjectType *ot = CastToObjectType(t); + ot->DestroyInternal(); + ot->ReleaseInternal(); + } + } + } + types.SetLength(0); + } + + // Release other config groups + for( n = 0; n < referencedConfigGroups.GetLength(); n++ ) + referencedConfigGroups[n]->refCount--; + referencedConfigGroups.SetLength(0); +} + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_context.cpp b/3rdparty/angelscript/src/as_context.cpp new file mode 100644 index 0000000..6912d72 --- /dev/null +++ b/3rdparty/angelscript/src/as_context.cpp @@ -0,0 +1,5780 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_context.cpp +// +// This class handles the execution of the byte code +// + +#include // fmodf() pow() + +#include "as_config.h" +#include "as_context.h" +#include "as_scriptengine.h" +#include "as_tokendef.h" +#include "as_texts.h" +#include "as_callfunc.h" +#include "as_generic.h" +#include "as_debug.h" // mkdir() +#include "as_bytecode.h" +#include "as_scriptobject.h" + +#ifdef _MSC_VER +#pragma warning(disable:4702) // unreachable code +#endif + +BEGIN_AS_NAMESPACE + +// We need at least 2 PTRs reserved for exception handling +// We need at least 1 PTR reserved for calling system functions +const int RESERVE_STACK = 2*AS_PTR_SIZE; + +// For each script function call we push 9 PTRs on the call stack +const int CALLSTACK_FRAME_SIZE = 9; + +#if defined(AS_DEBUG) + +class asCDebugStats +{ +public: + asCDebugStats() + { + memset(instrCount, 0, sizeof(instrCount)); + memset(instrCount2, 0, sizeof(instrCount2)); + lastBC = 255; + } + + ~asCDebugStats() + { + // This code writes out some statistics for the VM. + // It's useful for determining what needs to be optimized. + + _mkdir("AS_DEBUG"); + #if _MSC_VER >= 1500 && !defined(AS_MARMALADE) + FILE *f; + fopen_s(&f, "AS_DEBUG/stats.txt", "wt"); + #else + FILE *f = fopen("AS_DEBUG/stats.txt", "wt"); + #endif + if( f ) + { + // Output instruction statistics + fprintf(f, "\nTotal count\n"); + int n; + for( n = 0; n < asBC_MAXBYTECODE; n++ ) + { + if( asBCInfo[n].name && instrCount[n] > 0 ) + fprintf(f, "%-10.10s : %.0f\n", asBCInfo[n].name, instrCount[n]); + } + + fprintf(f, "\nNever executed\n"); + for( n = 0; n < asBC_MAXBYTECODE; n++ ) + { + if( asBCInfo[n].name && instrCount[n] == 0 ) + fprintf(f, "%-10.10s\n", asBCInfo[n].name); + } + + fprintf(f, "\nSequences\n"); + for( n = 0; n < 256; n++ ) + { + if( asBCInfo[n].name ) + { + for( int m = 0; m < 256; m++ ) + { + if( instrCount2[n][m] ) + fprintf(f, "%-10.10s, %-10.10s : %.0f\n", asBCInfo[n].name, asBCInfo[m].name, instrCount2[n][m]); + } + } + } + fclose(f); + } + } + + void Instr(asBYTE bc) + { + ++instrCount[bc]; + ++instrCount2[lastBC][bc]; + lastBC = bc; + } + + // Instruction statistics + double instrCount[256]; + double instrCount2[256][256]; + int lastBC; +} stats; + +#endif + +// interface +AS_API asIScriptContext *asGetActiveContext() +{ + asCThreadLocalData *tld = asCThreadManager::GetLocalData(); + + // tld can be 0 if asGetActiveContext is called before any engine has been created. + + // Observe! I've seen a case where an application linked with the library twice + // and thus ended up with two separate instances of the code and global variables. + // The application somehow mixed the two instances so that a function called from + // a script ended up calling asGetActiveContext from the other instance that had + // never been initialized. + + if( tld == 0 || tld->activeContexts.GetLength() == 0 ) + return 0; + return tld->activeContexts[tld->activeContexts.GetLength()-1]; +} + +// internal +// Note: There is no asPopActiveContext(), just call tld->activeContexts.PopLast() instead +asCThreadLocalData *asPushActiveContext(asIScriptContext *ctx) +{ + asCThreadLocalData *tld = asCThreadManager::GetLocalData(); + asASSERT( tld ); + if( tld == 0 ) + return 0; + tld->activeContexts.PushLast(ctx); + return tld; +} + +asCContext::asCContext(asCScriptEngine *engine, bool holdRef) +{ + m_refCount.set(1); + + m_holdEngineRef = holdRef; + if( holdRef ) + engine->AddRef(); + + m_engine = engine; + m_status = asEXECUTION_UNINITIALIZED; + m_stackBlockSize = 0; + m_originalStackPointer = 0; + m_inExceptionHandler = false; + m_isStackMemoryNotAllocated = false; + m_needToCleanupArgs = false; + m_currentFunction = 0; + m_callingSystemFunction = 0; + m_regs.objectRegister = 0; + m_initialFunction = 0; + m_lineCallback = false; + m_exceptionCallback = false; + m_regs.doProcessSuspend = false; + m_doSuspend = false; + m_userData = 0; + m_regs.ctx = this; +} + +asCContext::~asCContext() +{ + DetachEngine(); +} + +// interface +bool asCContext::IsNested(asUINT *nestCount) const +{ + if( nestCount ) + *nestCount = 0; + + asUINT c = GetCallstackSize(); + if( c == 0 ) + return false; + + // Search for a marker on the call stack + // This loop starts at 2 because the 0th entry is not stored in m_callStack, + // and then we need to subtract one more to get the base of each frame + for( asUINT n = 2; n <= c; n++ ) + { + const asPWORD *s = m_callStack.AddressOf() + (c - n)*CALLSTACK_FRAME_SIZE; + if( s && s[0] == 0 ) + { + if( nestCount ) + (*nestCount)++; + else + return true; + } + } + + if( nestCount && *nestCount > 0 ) + return true; + + return false; +} + +// interface +int asCContext::AddRef() const +{ + return m_refCount.atomicInc(); +} + +// interface +int asCContext::Release() const +{ + int r = m_refCount.atomicDec(); + + if( r == 0 ) + { + asDELETE(const_cast(this),asCContext); + return 0; + } + + return r; +} + +// internal +void asCContext::DetachEngine() +{ + if( m_engine == 0 ) return; + + // Clean up all calls, included nested ones + do + { + // Abort any execution + Abort(); + + // Free all resources + Unprepare(); + } + while( IsNested() ); + + // Free the stack blocks + for( asUINT n = 0; n < m_stackBlocks.GetLength(); n++ ) + { + if( m_stackBlocks[n] ) + { +#ifndef WIP_16BYTE_ALIGN + asDELETEARRAY(m_stackBlocks[n]); +#else + asDELETEARRAYALIGNED(m_stackBlocks[n]); +#endif + } + } + m_stackBlocks.SetLength(0); + m_stackBlockSize = 0; + + // Clean the user data + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n+1] ) + { + for( asUINT c = 0; c < m_engine->cleanContextFuncs.GetLength(); c++ ) + if( m_engine->cleanContextFuncs[c].type == m_userData[n] ) + m_engine->cleanContextFuncs[c].cleanFunc(this); + } + } + m_userData.SetLength(0); + + // Clear engine pointer + if( m_holdEngineRef ) + m_engine->Release(); + m_engine = 0; +} + +// interface +asIScriptEngine *asCContext::GetEngine() const +{ + return m_engine; +} + +// interface +void *asCContext::SetUserData(void *data, asPWORD type) +{ + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(m_engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n] == type ) + { + void *oldData = reinterpret_cast(m_userData[n+1]); + m_userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(m_engine->engineRWLock); + + return oldData; + } + } + + m_userData.PushLast(type); + m_userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(m_engine->engineRWLock); + + return 0; +} + +// interface +void *asCContext::GetUserData(asPWORD type) const +{ + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(m_engine->engineRWLock); + + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n] == type ) + { + RELEASESHARED(m_engine->engineRWLock); + return reinterpret_cast(m_userData[n+1]); + } + } + + RELEASESHARED(m_engine->engineRWLock); + + return 0; +} + +// interface +asIScriptFunction *asCContext::GetSystemFunction() +{ + return m_callingSystemFunction; +} + +// interface +int asCContext::Prepare(asIScriptFunction *func) +{ + if( func == 0 ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", "null", asNO_FUNCTION); + m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return asNO_FUNCTION; + } + + if( m_status == asEXECUTION_ACTIVE || m_status == asEXECUTION_SUSPENDED ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asCONTEXT_ACTIVE); + m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return asCONTEXT_ACTIVE; + } + + // Clean the stack if not done before + if( m_status != asEXECUTION_FINISHED && m_status != asEXECUTION_UNINITIALIZED ) + CleanStack(); + + // Release the returned object (if any) + CleanReturnObject(); + + // Release the object if it is a script object + if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + { + asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0]; + if( obj ) + obj->Release(); + + *(asPWORD*)&m_regs.stackFramePointer[0] = 0; + } + + if( m_initialFunction && m_initialFunction == func ) + { + // If the same function is executed again, we can skip a lot of the setup + m_currentFunction = m_initialFunction; + + // Reset stack pointer + m_regs.stackPointer = m_originalStackPointer; + + // Make sure the stack pointer is pointing to the original position, + // otherwise something is wrong with the way it is being updated + asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) ); + } + else + { + asASSERT( m_engine ); + + // Make sure the function is from the same engine as the context to avoid mixups + if( m_engine != func->GetEngine() ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asINVALID_ARG); + m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return asINVALID_ARG; + } + + if( m_initialFunction ) + { + m_initialFunction->Release(); + + // Reset stack pointer + m_regs.stackPointer = m_originalStackPointer; + + // Make sure the stack pointer is pointing to the original position, + // otherwise something is wrong with the way it is being updated + asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) ); + } + + // We trust the application not to pass anything else but a asCScriptFunction + m_initialFunction = reinterpret_cast(func); + m_initialFunction->AddRef(); + m_currentFunction = m_initialFunction; + + // TODO: runtime optimize: GetSpaceNeededForArguments() should be precomputed + m_argumentsSize = m_currentFunction->GetSpaceNeededForArguments() + (m_currentFunction->objectType ? AS_PTR_SIZE : 0); + + // Reserve space for the arguments and return value + if( m_currentFunction->DoesReturnOnStack() ) + { + m_returnValueSize = m_currentFunction->returnType.GetSizeInMemoryDWords(); + m_argumentsSize += AS_PTR_SIZE; + } + else + m_returnValueSize = 0; + + // Determine the minimum stack size needed + int stackSize = m_argumentsSize + m_returnValueSize; + if( m_currentFunction->scriptData ) + stackSize += m_currentFunction->scriptData->stackNeeded; + + // Make sure there is enough space on the stack for the arguments and return value + if( !ReserveStackSpace(stackSize) ) + return asOUT_OF_MEMORY; + } + + // Reset state + // Most of the time the previous state will be asEXECUTION_FINISHED, in which case the values are already initialized + if( m_status != asEXECUTION_FINISHED ) + { + m_exceptionLine = -1; + m_exceptionFunction = 0; + m_doAbort = false; + m_doSuspend = false; + m_regs.doProcessSuspend = m_lineCallback; + m_externalSuspendRequest = false; + } + m_status = asEXECUTION_PREPARED; + m_regs.programPointer = 0; + + // Reserve space for the arguments and return value + m_regs.stackFramePointer = m_regs.stackPointer - m_argumentsSize - m_returnValueSize; + m_originalStackPointer = m_regs.stackPointer; + m_regs.stackPointer = m_regs.stackFramePointer; + + // Set arguments to 0 + memset(m_regs.stackPointer, 0, 4*m_argumentsSize); + + if( m_returnValueSize ) + { + // Set the address of the location where the return value should be put + asDWORD *ptr = m_regs.stackFramePointer; + if( m_currentFunction->objectType ) + ptr += AS_PTR_SIZE; + + *(void**)ptr = (void*)(m_regs.stackFramePointer + m_argumentsSize); + } + + return asSUCCESS; +} + +// Free all resources +int asCContext::Unprepare() +{ + if( m_status == asEXECUTION_ACTIVE || m_status == asEXECUTION_SUSPENDED ) + return asCONTEXT_ACTIVE; + + // Only clean the stack if the context was prepared but not executed until the end + if( m_status != asEXECUTION_UNINITIALIZED && + m_status != asEXECUTION_FINISHED ) + CleanStack(); + + asASSERT( m_needToCleanupArgs == false ); + + // Release the returned object (if any) + CleanReturnObject(); + + // Release the object if it is a script object + if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + { + asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0]; + if( obj ) + obj->Release(); + } + + // Release the initial function + if( m_initialFunction ) + { + m_initialFunction->Release(); + + // Reset stack pointer + m_regs.stackPointer = m_originalStackPointer; + + // Make sure the stack pointer is pointing to the original position, + // otherwise something is wrong with the way it is being updated + asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) ); + } + + // Clear function pointers + m_initialFunction = 0; + m_currentFunction = 0; + m_exceptionFunction = 0; + m_regs.programPointer = 0; + + // Reset status + m_status = asEXECUTION_UNINITIALIZED; + + m_regs.stackFramePointer = 0; + + return 0; +} + +asBYTE asCContext::GetReturnByte() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return *(asBYTE*)&m_regs.valueRegister; +} + +asWORD asCContext::GetReturnWord() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return *(asWORD*)&m_regs.valueRegister; +} + +asDWORD asCContext::GetReturnDWord() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return *(asDWORD*)&m_regs.valueRegister; +} + +asQWORD asCContext::GetReturnQWord() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return m_regs.valueRegister; +} + +float asCContext::GetReturnFloat() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return *(float*)&m_regs.valueRegister; +} + +double asCContext::GetReturnDouble() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0; + + return *(double*)&m_regs.valueRegister; +} + +void *asCContext::GetReturnAddress() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( dt->IsReference() ) + return *(void**)&m_regs.valueRegister; + else if( dt->IsObject() || dt->IsFuncdef() ) + { + if( m_initialFunction->DoesReturnOnStack() ) + { + // The address of the return value was passed as the first argument, after the object pointer + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + return *(void**)(&m_regs.stackFramePointer[offset]); + } + + return m_regs.objectRegister; + } + + return 0; +} + +void *asCContext::GetReturnObject() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + if( !dt->IsObject() && !dt->IsFuncdef() ) return 0; + + if( dt->IsReference() ) + return *(void**)(asPWORD)m_regs.valueRegister; + else + { + if( m_initialFunction->DoesReturnOnStack() ) + { + // The address of the return value was passed as the first argument, after the object pointer + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + return *(void**)(&m_regs.stackFramePointer[offset]); + } + + return m_regs.objectRegister; + } +} + +void *asCContext::GetAddressOfReturnValue() +{ + if( m_status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &m_initialFunction->returnType; + + // An object is stored in the objectRegister + if( !dt->IsReference() && (dt->IsObject() || dt->IsFuncdef()) ) + { + // Need to dereference objects + if( !dt->IsObjectHandle() ) + { + if( m_initialFunction->DoesReturnOnStack() ) + { + // The address of the return value was passed as the first argument, after the object pointer + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + return *(void**)(&m_regs.stackFramePointer[offset]); + } + + return *(void**)&m_regs.objectRegister; + } + return &m_regs.objectRegister; + } + + // Primitives and references are stored in valueRegister + return &m_regs.valueRegister; +} + +int asCContext::SetObject(void *obj) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( !m_initialFunction->objectType ) + { + m_status = asEXECUTION_ERROR; + return asERROR; + } + + asASSERT( *(asPWORD*)&m_regs.stackFramePointer[0] == 0 ); + + *(asPWORD*)&m_regs.stackFramePointer[0] = (asPWORD)obj; + + // TODO: This should be optional by having a flag where the application can chose whether it should be done or not + // The flag could be named something like takeOwnership and have default value of true + if( obj && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + reinterpret_cast(obj)->AddRef(); + + return 0; +} + +int asCContext::SetArgByte(asUINT arg, asBYTE value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 1 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asBYTE*)&m_regs.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgWord(asUINT arg, asWORD value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 2 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asWORD*)&m_regs.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgDWord(asUINT arg, asDWORD value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 4 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asDWORD*)&m_regs.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgQWord(asUINT arg, asQWORD value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 2 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asQWORD*)(&m_regs.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgFloat(asUINT arg, float value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 1 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(float*)(&m_regs.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgDouble(asUINT arg, double value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 2 ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(double*)(&m_regs.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgAddress(asUINT arg, void *value) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( !dt->IsReference() && !dt->IsObjectHandle() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)value; + + return 0; +} + +int asCContext::SetArgObject(asUINT arg, void *obj) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( !dt->IsObject() && !dt->IsFuncdef() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // If the object should be sent by value we must make a copy of it + if( !dt->IsReference() ) + { + if( dt->IsObjectHandle() ) + { + // Increase the reference counter + if (obj && dt->IsFuncdef()) + ((asIScriptFunction*)obj)->AddRef(); + else + { + asSTypeBehaviour *beh = &CastToObjectType(dt->GetTypeInfo())->beh; + if (obj && beh->addref) + m_engine->CallObjectMethod(obj, beh->addref); + } + } + else + { + obj = m_engine->CreateScriptObjectCopy(obj, dt->GetTypeInfo()); + } + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)obj; + + return 0; +} + +int asCContext::SetArgVarType(asUINT arg, void *ptr, int typeId) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->GetTokenType() != ttQuestion ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the typeId and pointer + *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)ptr; + offset += AS_PTR_SIZE; + *(int*)(&m_regs.stackFramePointer[offset]) = typeId; + + return 0; +} + +// TODO: Instead of GetAddressOfArg, maybe we need a SetArgValue(int arg, void *value, bool takeOwnership) instead. + +// interface +void *asCContext::GetAddressOfArg(asUINT arg) +{ + if( m_status != asEXECUTION_PREPARED ) + return 0; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + return 0; + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // We should return the address of the location where the argument value will be placed + + // All registered types are always sent by reference, even if + // the function is declared to receive the argument by value. + return &m_regs.stackFramePointer[offset]; +} + + +int asCContext::Abort() +{ + if( m_engine == 0 ) return asERROR; + + // TODO: multithread: Make thread safe. There is a chance that the status + // changes to something else after being set to ABORTED here. + if( m_status == asEXECUTION_SUSPENDED ) + m_status = asEXECUTION_ABORTED; + + m_doSuspend = true; + m_regs.doProcessSuspend = true; + m_externalSuspendRequest = true; + m_doAbort = true; + + return 0; +} + +// interface +int asCContext::Suspend() +{ + // This function just sets some internal flags and is safe + // to call from a secondary thread, even if the library has + // been built without multi-thread support. + + if( m_engine == 0 ) return asERROR; + + m_doSuspend = true; + m_externalSuspendRequest = true; + m_regs.doProcessSuspend = true; + + return 0; +} + +// interface +int asCContext::Execute() +{ + asASSERT( m_engine != 0 ); + + if( m_status != asEXECUTION_SUSPENDED && m_status != asEXECUTION_PREPARED ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_d, "Execute", asCONTEXT_NOT_PREPARED); + m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return asCONTEXT_NOT_PREPARED; + } + + m_status = asEXECUTION_ACTIVE; + + asCThreadLocalData *tld = asPushActiveContext((asIScriptContext *)this); + + if( m_regs.programPointer == 0 ) + { + if( m_currentFunction->funcType == asFUNC_DELEGATE ) + { + // Push the object pointer onto the stack + asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] ); + m_regs.stackPointer -= AS_PTR_SIZE; + m_regs.stackFramePointer -= AS_PTR_SIZE; + *(asPWORD*)m_regs.stackPointer = asPWORD(m_currentFunction->objForDelegate); + + // Make the call to the delegated object method + m_currentFunction = m_currentFunction->funcForDelegate; + } + + if( m_currentFunction->funcType == asFUNC_VIRTUAL || + m_currentFunction->funcType == asFUNC_INTERFACE ) + { + // The currentFunction is a virtual method + + // Determine the true function from the object + asCScriptObject *obj = *(asCScriptObject**)(asPWORD*)m_regs.stackFramePointer; + if( obj == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + } + else + { + asCObjectType *objType = obj->objType; + asCScriptFunction *realFunc = 0; + + if( m_currentFunction->funcType == asFUNC_VIRTUAL ) + { + if( objType->virtualFunctionTable.GetLength() > (asUINT)m_currentFunction->vfTableIdx ) + { + realFunc = objType->virtualFunctionTable[m_currentFunction->vfTableIdx]; + } + } + else + { + // Search the object type for a function that matches the interface function + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *f2 = m_engine->scriptFunctions[objType->methods[n]]; + if( f2->signatureId == m_currentFunction->signatureId ) + { + if( f2->funcType == asFUNC_VIRTUAL ) + realFunc = objType->virtualFunctionTable[f2->vfTableIdx]; + else + realFunc = f2; + break; + } + } + } + + if( realFunc && realFunc->signatureId == m_currentFunction->signatureId ) + m_currentFunction = realFunc; + else + SetInternalException(TXT_NULL_POINTER_ACCESS); + } + } + else if( m_currentFunction->funcType == asFUNC_IMPORTED ) + { + int funcId = m_engine->importedFunctions[m_currentFunction->id & ~FUNC_IMPORTED]->boundFunctionId; + if( funcId > 0 ) + m_currentFunction = m_engine->scriptFunctions[funcId]; + else + SetInternalException(TXT_UNBOUND_FUNCTION); + } + + if( m_currentFunction->funcType == asFUNC_SCRIPT ) + { + m_regs.programPointer = m_currentFunction->scriptData->byteCode.AddressOf(); + + // Set up the internal registers for executing the script function + PrepareScriptFunction(); + } + else if( m_currentFunction->funcType == asFUNC_SYSTEM ) + { + // The current function is an application registered function + + // Call the function directly + CallSystemFunction(m_currentFunction->id, this); + + // Was the call successful? + if( m_status == asEXECUTION_ACTIVE ) + { + m_status = asEXECUTION_FINISHED; + } + } + else + { + // This shouldn't happen unless there was an error in which + // case an exception should have been raised already + asASSERT( m_status == asEXECUTION_EXCEPTION ); + } + } + + asUINT gcPreObjects = 0; + if( m_engine->ep.autoGarbageCollect ) + m_engine->gc.GetStatistics(&gcPreObjects, 0, 0, 0, 0); + + while( m_status == asEXECUTION_ACTIVE ) + ExecuteNext(); + + if( m_lineCallback ) + { + // Call the line callback one last time before leaving + // so anyone listening can catch the state change + CallLineCallback(); + m_regs.doProcessSuspend = true; + } + else + m_regs.doProcessSuspend = false; + + m_doSuspend = false; + + if( m_engine->ep.autoGarbageCollect ) + { + asUINT gcPosObjects = 0; + m_engine->gc.GetStatistics(&gcPosObjects, 0, 0, 0, 0); + if( gcPosObjects > gcPreObjects ) + { + // Execute as many steps as there were new objects created + m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, gcPosObjects - gcPreObjects); + } + else if( gcPosObjects > 0 ) + { + // Execute at least one step, even if no new objects were created + m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, 1); + } + } + + // Pop the active context + asASSERT(tld && tld->activeContexts[tld->activeContexts.GetLength()-1] == this); + if( tld ) + tld->activeContexts.PopLast(); + + if( m_status == asEXECUTION_FINISHED ) + { + m_regs.objectType = m_initialFunction->returnType.GetTypeInfo(); + return asEXECUTION_FINISHED; + } + + if( m_doAbort ) + { + m_doAbort = false; + + m_status = asEXECUTION_ABORTED; + return asEXECUTION_ABORTED; + } + + if( m_status == asEXECUTION_SUSPENDED ) + return asEXECUTION_SUSPENDED; + + if( m_status == asEXECUTION_EXCEPTION ) + return asEXECUTION_EXCEPTION; + + return asERROR; +} + +int asCContext::PushState() +{ + // Only allow the state to be pushed when active + // TODO: Can we support a suspended state too? So the reuse of + // the context can be done outside the Execute() call? + if( m_status != asEXECUTION_ACTIVE ) + { + // TODO: Write message. Wrong usage + return asERROR; + } + + // Push the current script function that is calling the system function + PushCallState(); + + // Push the system function too, which will serve both as a marker and + // informing which system function that created the nested call + if( m_callStack.GetLength() == m_callStack.GetCapacity() ) + { + // Allocate space for 10 call states at a time to save time + m_callStack.AllocateNoConstruct(m_callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true); + } + m_callStack.SetLengthNoConstruct(m_callStack.GetLength() + CALLSTACK_FRAME_SIZE); + + // Need to push m_initialFunction as it must be restored later + asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE; + tmp[0] = 0; + tmp[1] = (asPWORD)m_callingSystemFunction; + tmp[2] = (asPWORD)m_initialFunction; + tmp[3] = (asPWORD)m_originalStackPointer; + tmp[4] = (asPWORD)m_argumentsSize; + + // Need to push the value of registers so they can be restored + tmp[5] = (asPWORD)asDWORD(m_regs.valueRegister); + tmp[6] = (asPWORD)asDWORD(m_regs.valueRegister>>32); + tmp[7] = (asPWORD)m_regs.objectRegister; + tmp[8] = (asPWORD)m_regs.objectType; + + // Decrease stackpointer to prevent the top value from being overwritten + m_regs.stackPointer -= 2; + + // Clear the initial function so that Prepare() knows it must do all validations + m_initialFunction = 0; + + // After this the state should appear as if uninitialized + m_callingSystemFunction = 0; + + m_regs.objectRegister = 0; + m_regs.objectType = 0; + + // Set the status to uninitialized as application + // should call Prepare() after this to reuse the context + m_status = asEXECUTION_UNINITIALIZED; + + return asSUCCESS; +} + +int asCContext::PopState() +{ + if( !IsNested() ) + return asERROR; + + // Clean up the current execution + Unprepare(); + + // The topmost state must be a marker for nested call + asASSERT( m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE] == 0 ); + + // Restore the previous state + asPWORD *tmp = &m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE]; + m_callingSystemFunction = reinterpret_cast(tmp[1]); + m_callStack.SetLength(m_callStack.GetLength() - CALLSTACK_FRAME_SIZE); + + // Restore the previous initial function and the associated values + m_initialFunction = reinterpret_cast(tmp[2]); + m_originalStackPointer = (asDWORD*)tmp[3]; + m_argumentsSize = (int)tmp[4]; + + m_regs.valueRegister = asQWORD(asDWORD(tmp[5])); + m_regs.valueRegister |= asQWORD(tmp[6])<<32; + m_regs.objectRegister = (void*)tmp[7]; + m_regs.objectType = (asITypeInfo*)tmp[8]; + + // Calculate the returnValueSize + if( m_initialFunction->DoesReturnOnStack() ) + m_returnValueSize = m_initialFunction->returnType.GetSizeInMemoryDWords(); + else + m_returnValueSize = 0; + + // Pop the current script function. This will also restore the previous stack pointer + PopCallState(); + + m_status = asEXECUTION_ACTIVE; + + return asSUCCESS; +} + +void asCContext::PushCallState() +{ + if( m_callStack.GetLength() == m_callStack.GetCapacity() ) + { + // Allocate space for 10 call states at a time to save time + m_callStack.AllocateNoConstruct(m_callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true); + } + m_callStack.SetLengthNoConstruct(m_callStack.GetLength() + CALLSTACK_FRAME_SIZE); + + // Separating the loads and stores limits data cache trash, and with a smart compiler + // could turn into SIMD style loading/storing if available. + // The compiler can't do this itself due to potential pointer aliasing between the pointers, + // ie writing to tmp could overwrite the data contained in registers.stackFramePointer for example + // for all the compiler knows. So introducing the local variable s, which is never referred to by + // its address we avoid this issue. + + asPWORD s[5]; + s[0] = (asPWORD)m_regs.stackFramePointer; + s[1] = (asPWORD)m_currentFunction; + s[2] = (asPWORD)m_regs.programPointer; + s[3] = (asPWORD)m_regs.stackPointer; + s[4] = m_stackIndex; + + asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE; + tmp[0] = s[0]; + tmp[1] = s[1]; + tmp[2] = s[2]; + tmp[3] = s[3]; + tmp[4] = s[4]; +} + +void asCContext::PopCallState() +{ + // See comments in PushCallState about pointer aliasing and data cache trashing + asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE; + asPWORD s[5]; + s[0] = tmp[0]; + s[1] = tmp[1]; + s[2] = tmp[2]; + s[3] = tmp[3]; + s[4] = tmp[4]; + + m_regs.stackFramePointer = (asDWORD*)s[0]; + m_currentFunction = (asCScriptFunction*)s[1]; + m_regs.programPointer = (asDWORD*)s[2]; + m_regs.stackPointer = (asDWORD*)s[3]; + m_stackIndex = (int)s[4]; + + m_callStack.SetLength(m_callStack.GetLength() - CALLSTACK_FRAME_SIZE); +} + +// interface +asUINT asCContext::GetCallstackSize() const +{ + if( m_currentFunction == 0 ) return 0; + + // The current function is accessed at stackLevel 0 + return asUINT(1 + m_callStack.GetLength() / CALLSTACK_FRAME_SIZE); +} + +// interface +asIScriptFunction *asCContext::GetFunction(asUINT stackLevel) +{ + if( stackLevel >= GetCallstackSize() ) return 0; + + if( stackLevel == 0 ) return m_currentFunction; + + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize() - stackLevel - 1)*CALLSTACK_FRAME_SIZE; + asCScriptFunction *func = (asCScriptFunction*)s[1]; + + return func; +} + +// interface +int asCContext::GetLineNumber(asUINT stackLevel, int *column, const char **sectionName) +{ + if( stackLevel >= GetCallstackSize() ) return asINVALID_ARG; + + asCScriptFunction *func; + asDWORD *bytePos; + if( stackLevel == 0 ) + { + func = m_currentFunction; + if( func->scriptData == 0 ) return 0; + bytePos = m_regs.programPointer; + } + else + { + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + if( func->scriptData == 0 ) return 0; + bytePos = (asDWORD*)s[2]; + + // Subract 1 from the bytePos, because we want the line where + // the call was made, and not the instruction after the call + bytePos -= 1; + } + + // For nested calls it is possible that func is null + if( func == 0 ) + { + if( column ) *column = 0; + if( sectionName ) *sectionName = 0; + return 0; + } + + int sectionIdx; + asDWORD line = func->GetLineNumber(int(bytePos - func->scriptData->byteCode.AddressOf()), §ionIdx); + if( column ) *column = (line >> 20); + if( sectionName ) + { + asASSERT( sectionIdx < int(m_engine->scriptSectionNames.GetLength()) ); + if( sectionIdx >= 0 && asUINT(sectionIdx) < m_engine->scriptSectionNames.GetLength() ) + *sectionName = m_engine->scriptSectionNames[sectionIdx]->AddressOf(); + else + *sectionName = 0; + } + return (line & 0xFFFFF); +} + +// internal +bool asCContext::ReserveStackSpace(asUINT size) +{ +#ifdef WIP_16BYTE_ALIGN + // Pad size to a multiple of MAX_TYPE_ALIGNMENT. + const asUINT remainder = size % MAX_TYPE_ALIGNMENT; + if(remainder != 0) + { + size = size + (MAX_TYPE_ALIGNMENT - (size % MAX_TYPE_ALIGNMENT)); + } +#endif + + // Make sure the first stack block is allocated + if( m_stackBlocks.GetLength() == 0 ) + { + m_stackBlockSize = m_engine->initialContextStackSize; + asASSERT( m_stackBlockSize > 0 ); + +#ifndef WIP_16BYTE_ALIGN + asDWORD *stack = asNEWARRAY(asDWORD,m_stackBlockSize); +#else + asDWORD *stack = asNEWARRAYALIGNED(asDWORD, m_stackBlockSize, MAX_TYPE_ALIGNMENT); +#endif + if( stack == 0 ) + { + // Out of memory + return false; + } + +#ifdef WIP_16BYTE_ALIGN + asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) ); +#endif + + m_stackBlocks.PushLast(stack); + m_stackIndex = 0; + m_regs.stackPointer = m_stackBlocks[0] + m_stackBlockSize; + +#ifdef WIP_16BYTE_ALIGN + // Align the stack pointer. This is necessary as the m_stackBlockSize is not necessarily evenly divisable with the max alignment + ((asPWORD&)m_regs.stackPointer) &= ~(MAX_TYPE_ALIGNMENT-1); + + asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) ); +#endif + } + + // Check if there is enough space on the current stack block, otherwise move + // to the next one. New and larger blocks will be allocated as necessary + while( m_regs.stackPointer - (size + RESERVE_STACK) < m_stackBlocks[m_stackIndex] ) + { + // Make sure we don't allocate more space than allowed + if( m_engine->ep.maximumContextStackSize ) + { + // This test will only stop growth once it has already crossed the limit + if( m_stackBlockSize * ((1 << (m_stackIndex+1)) - 1) > m_engine->ep.maximumContextStackSize ) + { + m_isStackMemoryNotAllocated = true; + + // Set the stackFramePointer, even though the stackPointer wasn't updated + m_regs.stackFramePointer = m_regs.stackPointer; + + SetInternalException(TXT_STACK_OVERFLOW); + return false; + } + } + + m_stackIndex++; + if( m_stackBlocks.GetLength() == m_stackIndex ) + { + // Allocate the new stack block, with twice the size of the previous +#ifndef WIP_16BYTE_ALIGN + asDWORD *stack = asNEWARRAY(asDWORD, (m_stackBlockSize << m_stackIndex)); +#else + asDWORD *stack = asNEWARRAYALIGNED(asDWORD, (m_stackBlockSize << m_stackIndex), MAX_TYPE_ALIGNMENT); +#endif + if( stack == 0 ) + { + // Out of memory + m_isStackMemoryNotAllocated = true; + + // Set the stackFramePointer, even though the stackPointer wasn't updated + m_regs.stackFramePointer = m_regs.stackPointer; + + SetInternalException(TXT_STACK_OVERFLOW); + return false; + } + +#ifdef WIP_16BYTE_ALIGN + asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) ); +#endif + + m_stackBlocks.PushLast(stack); + } + + // Update the stack pointer to point to the new block. + // Leave enough room above the stackpointer to copy the arguments from the previous stackblock + m_regs.stackPointer = m_stackBlocks[m_stackIndex] + + (m_stackBlockSize<GetSpaceNeededForArguments() - + (m_currentFunction->objectType ? AS_PTR_SIZE : 0) - + (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0); + +#ifdef WIP_16BYTE_ALIGN + // Align the stack pointer + (asPWORD&)m_regs.stackPointer &= ~(MAX_TYPE_ALIGNMENT-1); + + asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) ); +#endif + } + + return true; +} + +// internal +void asCContext::CallScriptFunction(asCScriptFunction *func) +{ + asASSERT( func->scriptData ); + + // Push the framepointer, function id and programCounter on the stack + PushCallState(); + + // Update the current function and program position before increasing the stack + // so the exception handler will know what to do if there is a stack overflow + m_currentFunction = func; + m_regs.programPointer = m_currentFunction->scriptData->byteCode.AddressOf(); + + PrepareScriptFunction(); +} + +void asCContext::PrepareScriptFunction() +{ + asASSERT( m_currentFunction->scriptData ); + + // Make sure there is space on the stack to execute the function + asDWORD *oldStackPointer = m_regs.stackPointer; + if( !ReserveStackSpace(m_currentFunction->scriptData->stackNeeded) ) + return; + + // If a new stack block was allocated then we'll need to move + // over the function arguments to the new block. + if( m_regs.stackPointer != oldStackPointer ) + { + int numDwords = m_currentFunction->GetSpaceNeededForArguments() + + (m_currentFunction->objectType ? AS_PTR_SIZE : 0) + + (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0); + memcpy(m_regs.stackPointer, oldStackPointer, sizeof(asDWORD)*numDwords); + } + + // Update framepointer + m_regs.stackFramePointer = m_regs.stackPointer; + + // Set all object variables to 0 to guarantee that they are null before they are used + // Only variables on the heap should be cleared. The rest will be cleared by calling the constructor + asUINT n = m_currentFunction->scriptData->objVariablesOnHeap; + while( n-- > 0 ) + { + int pos = m_currentFunction->scriptData->objVariablePos[n]; + *(asPWORD*)&m_regs.stackFramePointer[-pos] = 0; + } + + // Initialize the stack pointer with the space needed for local variables + m_regs.stackPointer -= m_currentFunction->scriptData->variableSpace; + + // Call the line callback for each script function, to guarantee that infinitely recursive scripts can + // be interrupted, even if the scripts have been compiled with asEP_BUILD_WITHOUT_LINE_CUES + if( m_regs.doProcessSuspend ) + { + if( m_lineCallback ) + CallLineCallback(); + if( m_doSuspend ) + m_status = asEXECUTION_SUSPENDED; + } +} + +void asCContext::CallInterfaceMethod(asCScriptFunction *func) +{ + // Resolve the interface method using the current script type + asCScriptObject *obj = *(asCScriptObject**)(asPWORD*)m_regs.stackPointer; + if( obj == 0 ) + { + // Tell the exception handler to clean up the arguments to this method + m_needToCleanupArgs = true; + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + + asCObjectType *objType = obj->objType; + + // Search the object type for a function that matches the interface function + asCScriptFunction *realFunc = 0; + if( func->funcType == asFUNC_INTERFACE ) + { + // Find the offset for the interface's virtual function table chunk + asUINT offset = 0; + bool found = false; + asCObjectType *findInterface = func->objectType; + + // TODO: runtime optimize: The list of interfaces should be ordered by the address + // Then a binary search pattern can be used. + asUINT intfCount = asUINT(objType->interfaces.GetLength()); + for( asUINT n = 0; n < intfCount; n++ ) + { + if( objType->interfaces[n] == findInterface ) + { + offset = objType->interfaceVFTOffsets[n]; + found = true; + break; + } + } + + if( !found ) + { + // Tell the exception handler to clean up the arguments to this method + m_needToCleanupArgs = true; + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + + // Find the real function in the virtual table chunk with the found offset + realFunc = objType->virtualFunctionTable[func->vfTableIdx + offset]; + + // Since the interface was implemented by the class, it shouldn't + // be possible that the real function isn't found + asASSERT( realFunc ); + + asASSERT( realFunc->signatureId == func->signatureId ); + } + else // if( func->funcType == asFUNC_VIRTUAL ) + { + realFunc = objType->virtualFunctionTable[func->vfTableIdx]; + } + + // Then call the true script function + CallScriptFunction(realFunc); +} + +void asCContext::ExecuteNext() +{ + asDWORD *l_bc = m_regs.programPointer; + asDWORD *l_sp = m_regs.stackPointer; + asDWORD *l_fp = m_regs.stackFramePointer; + + for(;;) + { + +#ifdef AS_DEBUG + // Gather statistics on executed bytecode + stats.Instr(*(asBYTE*)l_bc); + + // Used to verify that the size of the instructions are correct + asDWORD *old = l_bc; +#endif + + + // Remember to keep the cases in order and without + // gaps, because that will make the switch faster. + // It will be faster since only one lookup will be + // made to find the correct jump destination. If not + // in order, the switch will make two lookups. + switch( *(asBYTE*)l_bc ) + { +//-------------- +// memory access functions + + case asBC_PopPtr: + // Pop a pointer from the stack + l_sp += AS_PTR_SIZE; + l_bc++; + break; + + case asBC_PshGPtr: + // Replaces PGA + RDSPtr + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = *(asPWORD*)asBC_PTRARG(l_bc); + l_bc += 1 + AS_PTR_SIZE; + break; + + // Push a dword value on the stack + case asBC_PshC4: + --l_sp; + *l_sp = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + // Push the dword value of a variable on the stack + case asBC_PshV4: + --l_sp; + *l_sp = *(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Push the address of a variable on the stack + case asBC_PSF: + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = asPWORD(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Swap the top 2 pointers on the stack + case asBC_SwapPtr: + { + asPWORD p = *(asPWORD*)l_sp; + *(asPWORD*)l_sp = *(asPWORD*)(l_sp+AS_PTR_SIZE); + *(asPWORD*)(l_sp+AS_PTR_SIZE) = p; + l_bc++; + } + break; + + // Do a boolean not operation, modifying the value of the variable + case asBC_NOT: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on the pointer. + + volatile asBYTE *ptr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + asBYTE val = (ptr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + ptr[0] = val; // The result is stored in the lower byte + ptr[1] = 0; // Make sure the rest of the DWORD is 0 + ptr[2] = 0; + ptr[3] = 0; + } +#else + *(l_fp - asBC_SWORDARG0(l_bc)) = (*(l_fp - asBC_SWORDARG0(l_bc)) == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // Push the dword value of a global variable on the stack + case asBC_PshG4: + --l_sp; + *l_sp = *(asDWORD*)asBC_PTRARG(l_bc); + l_bc += 1 + AS_PTR_SIZE; + break; + + // Load the address of a global variable in the register, then + // copy the value of the global variable into a local variable + case asBC_LdGRdR4: + *(void**)&m_regs.valueRegister = (void*)asBC_PTRARG(l_bc); + *(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)&m_regs.valueRegister; + l_bc += 1+AS_PTR_SIZE; + break; + +//---------------- +// path control instructions + + // Begin execution of a script function + case asBC_CALL: + { + int i = asBC_INTARG(l_bc); + l_bc += 2; + + asASSERT( i >= 0 ); + asASSERT( (i & FUNC_IMPORTED) == 0 ); + + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + CallScriptFunction(m_engine->scriptFunctions[i]); + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + } + break; + + // Return to the caller, and remove the arguments from the stack + case asBC_RET: + { + // Return if this was the first function, or a nested execution + if( m_callStack.GetLength() == 0 || + m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE] == 0 ) + { + m_status = asEXECUTION_FINISHED; + return; + } + + asWORD w = asBC_WORDARG0(l_bc); + + // Read the old framepointer, functionid, and programCounter from the call stack + PopCallState(); + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // Pop arguments from stack + l_sp += w; + } + break; + + // Jump to a relative position + case asBC_JMP: + l_bc += 2 + asBC_INTARG(l_bc); + break; + +//---------------- +// Conditional jumps + + // Jump to a relative position if the value in the register is 0 + case asBC_JZ: + if( *(int*)&m_regs.valueRegister == 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is not 0 + case asBC_JNZ: + if( *(int*)&m_regs.valueRegister != 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is negative + case asBC_JS: + if( *(int*)&m_regs.valueRegister < 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register it not negative + case asBC_JNS: + if( *(int*)&m_regs.valueRegister >= 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is greater than 0 + case asBC_JP: + if( *(int*)&m_regs.valueRegister > 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is not greater than 0 + case asBC_JNP: + if( *(int*)&m_regs.valueRegister <= 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; +//-------------------- +// test instructions + + // If the value in the register is 0, then set the register to 1, else to 0 + case asBC_TZ: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not 0, then set the register to 1, else to 0 + case asBC_TNZ: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] == 0) ? 0 : VALUE_OF_BOOLEAN_TRUE; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister == 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + + // If the value in the register is negative, then set the register to 1, else to 0 + case asBC_TS: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is less than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] < 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister < 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not negative, then set the register to 1, else to 0 + case asBC_TNS: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not less than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] >= 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister < 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + + // If the value in the register is greater than 0, then set the register to 1, else to 0 + case asBC_TP: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is greater than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] > 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister > 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not greater than 0, then set the register to 1, else to 0 + case asBC_TNP: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not greater than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)&m_regs.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister; + asBYTE val = (regPtr[0] <= 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister > 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + +//-------------------- +// negate value + + // Negate the integer value in the variable + case asBC_NEGi: + *(l_fp - asBC_SWORDARG0(l_bc)) = asDWORD(-int(*(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + // Negate the float value in the variable + case asBC_NEGf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(float*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Negate the double value in the variable + case asBC_NEGd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(double*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + +//------------------------- +// Increment value pointed to by address in register + + // Increment the short value pointed to by the register + case asBC_INCi16: + (**(short**)&m_regs.valueRegister)++; + l_bc++; + break; + + // Increment the byte value pointed to by the register + case asBC_INCi8: + (**(char**)&m_regs.valueRegister)++; + l_bc++; + break; + + // Decrement the short value pointed to by the register + case asBC_DECi16: + (**(short**)&m_regs.valueRegister)--; + l_bc++; + break; + + // Decrement the byte value pointed to by the register + case asBC_DECi8: + (**(char**)&m_regs.valueRegister)--; + l_bc++; + break; + + // Increment the integer value pointed to by the register + case asBC_INCi: + ++(**(int**)&m_regs.valueRegister); + l_bc++; + break; + + // Decrement the integer value pointed to by the register + case asBC_DECi: + --(**(int**)&m_regs.valueRegister); + l_bc++; + break; + + // Increment the float value pointed to by the register + case asBC_INCf: + ++(**(float**)&m_regs.valueRegister); + l_bc++; + break; + + // Decrement the float value pointed to by the register + case asBC_DECf: + --(**(float**)&m_regs.valueRegister); + l_bc++; + break; + + // Increment the double value pointed to by the register + case asBC_INCd: + ++(**(double**)&m_regs.valueRegister); + l_bc++; + break; + + // Decrement the double value pointed to by the register + case asBC_DECd: + --(**(double**)&m_regs.valueRegister); + l_bc++; + break; + + // Increment the local integer variable + case asBC_IncVi: + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))++; + l_bc++; + break; + + // Decrement the local integer variable + case asBC_DecVi: + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))--; + l_bc++; + break; + +//-------------------- +// bits instructions + + // Do a bitwise not on the value in the variable + case asBC_BNOT: + *(l_fp - asBC_SWORDARG0(l_bc)) = ~*(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Do a bitwise and of two variables and store the result in a third variable + case asBC_BAND: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) & *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a bitwise or of two variables and store the result in a third variable + case asBC_BOR: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) | *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a bitwise xor of two variables and store the result in a third variable + case asBC_BXOR: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) ^ *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a logical shift left of two variables and store the result in a third variable + case asBC_BSLL: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a logical shift right of two variables and store the result in a third variable + case asBC_BSRL: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do an arithmetic shift right of two variables and store the result in a third variable + case asBC_BSRA: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(l_fp - asBC_SWORDARG1(l_bc))) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_COPY: + { + void *d = (void*)*(asPWORD*)l_sp; l_sp += AS_PTR_SIZE; + void *s = (void*)*(asPWORD*)l_sp; + if( s == 0 || d == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + memcpy(d, s, asBC_WORDARG0(l_bc)*4); + + // replace the pointer on the stack with the lvalue + *(asPWORD**)l_sp = (asPWORD*)d; + } + l_bc += 2; + break; + + case asBC_PshC8: + l_sp -= 2; + *(asQWORD*)l_sp = asBC_QWORDARG(l_bc); + l_bc += 3; + break; + + case asBC_PshVPtr: + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_RDSPtr: + { + // The pointer must not be null + asPWORD a = *(asPWORD*)l_sp; + if( a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + // Pop an address from the stack, read a pointer from that address and push it on the stack + *(asPWORD*)l_sp = *(asPWORD*)a; + } + l_bc++; + break; + + //---------------------------- + // Comparisons + case asBC_CMPd: + { + // Do a comparison of the values, rather than a subtraction + // in order to get proper behaviour for infinity values. + double dbl1 = *(double*)(l_fp - asBC_SWORDARG0(l_bc)); + double dbl2 = *(double*)(l_fp - asBC_SWORDARG1(l_bc)); + if( dbl1 == dbl2 ) *(int*)&m_regs.valueRegister = 0; + else if( dbl1 < dbl2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPu: + { + asDWORD d1 = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asDWORD d2 = *(asDWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0; + else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPf: + { + // Do a comparison of the values, rather than a subtraction + // in order to get proper behaviour for infinity values. + float f1 = *(float*)(l_fp - asBC_SWORDARG0(l_bc)); + float f2 = *(float*)(l_fp - asBC_SWORDARG1(l_bc)); + if( f1 == f2 ) *(int*)&m_regs.valueRegister = 0; + else if( f1 < f2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPi: + { + int i1 = *(int*)(l_fp - asBC_SWORDARG0(l_bc)); + int i2 = *(int*)(l_fp - asBC_SWORDARG1(l_bc)); + if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0; + else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + //---------------------------- + // Comparisons with constant value + case asBC_CMPIi: + { + int i1 = *(int*)(l_fp - asBC_SWORDARG0(l_bc)); + int i2 = asBC_INTARG(l_bc); + if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0; + else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPIf: + { + // Do a comparison of the values, rather than a subtraction + // in order to get proper behaviour for infinity values. + float f1 = *(float*)(l_fp - asBC_SWORDARG0(l_bc)); + float f2 = asBC_FLOATARG(l_bc); + if( f1 == f2 ) *(int*)&m_regs.valueRegister = 0; + else if( f1 < f2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPIu: + { + asDWORD d1 = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asDWORD d2 = asBC_DWORDARG(l_bc); + if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0; + else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_JMPP: + l_bc += 1 + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))*2; + break; + + case asBC_PopRPtr: + *(asPWORD*)&m_regs.valueRegister = *(asPWORD*)l_sp; + l_sp += AS_PTR_SIZE; + l_bc++; + break; + + case asBC_PshRPtr: + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = *(asPWORD*)&m_regs.valueRegister; + l_bc++; + break; + + case asBC_STR: + { + // Get the string id from the argument + asWORD w = asBC_WORDARG0(l_bc); + // Push the string pointer on the stack + const asCString &b = m_engine->GetConstantString(w); + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = (asPWORD)b.AddressOf(); + // Push the string length on the stack + --l_sp; + *l_sp = (asDWORD)b.GetLength(); + l_bc++; + } + break; + + case asBC_CALLSYS: + { + // Get function ID from the argument + int i = asBC_INTARG(l_bc); + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + l_sp += CallSystemFunction(i, this); + + // Update the program position after the call so that line number is correct + l_bc += 2; + + if( m_regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( m_doSuspend ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( m_status != asEXECUTION_ACTIVE ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + return; + } + } + } + break; + + case asBC_CALLBND: + { + // TODO: Clean-up: This code is very similar to asBC_CallPtr. Create a shared method for them + // Get the function ID from the stack + int i = asBC_INTARG(l_bc); + + asASSERT( i >= 0 ); + asASSERT( i & FUNC_IMPORTED ); + + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + int funcId = m_engine->importedFunctions[i & ~FUNC_IMPORTED]->boundFunctionId; + if( funcId == -1 ) + { + // Need to update the program pointer for the exception handler + m_regs.programPointer += 2; + + // Tell the exception handler to clean up the arguments to this function + m_needToCleanupArgs = true; + SetInternalException(TXT_UNBOUND_FUNCTION); + return; + } + else + { + asCScriptFunction *func = m_engine->GetScriptFunction(funcId); + if( func->funcType == asFUNC_SCRIPT ) + { + m_regs.programPointer += 2; + CallScriptFunction(func); + } + else if( func->funcType == asFUNC_DELEGATE ) + { + // Push the object pointer on the stack. There is always a reserved space for this so + // we don't don't need to worry about overflowing the allocated memory buffer + asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] ); + m_regs.stackPointer -= AS_PTR_SIZE; + *(asPWORD*)m_regs.stackPointer = asPWORD(func->objForDelegate); + + // Call the delegated method + if( func->funcForDelegate->funcType == asFUNC_SYSTEM ) + { + m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer += 2; + } + else + { + m_regs.programPointer += 2; + + // TODO: run-time optimize: The true method could be figured out when creating the delegate + CallInterfaceMethod(func->funcForDelegate); + } + } + else + { + asASSERT( func->funcType == asFUNC_SYSTEM ); + + m_regs.stackPointer += CallSystemFunction(func->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer += 2; + } + } + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + } + break; + + case asBC_SUSPEND: + if( m_regs.doProcessSuspend ) + { + if( m_lineCallback ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + CallLineCallback(); + } + if( m_doSuspend ) + { + l_bc++; + + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + } + + l_bc++; + break; + + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc); + int func = asBC_INTARG(l_bc+AS_PTR_SIZE); + + if( objType->flags & asOBJ_SCRIPT_OBJECT ) + { + // Need to move the values back to the context as the construction + // of the script object may reuse the context for nested calls. + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Pre-allocate the memory + asDWORD *mem = (asDWORD*)m_engine->CallAlloc(objType); + + // Pre-initialize the memory by calling the constructor for asCScriptObject + ScriptObject_Construct(objType, (asCScriptObject*)mem); + + // Call the constructor to initalize the memory + asCScriptFunction *f = m_engine->scriptFunctions[func]; + + asDWORD **a = (asDWORD**)*(asPWORD*)(m_regs.stackPointer + f->GetSpaceNeededForArguments()); + if( a ) *a = mem; + + // Push the object pointer on the stack + m_regs.stackPointer -= AS_PTR_SIZE; + *(asPWORD*)m_regs.stackPointer = (asPWORD)mem; + + m_regs.programPointer += 2+AS_PTR_SIZE; + + CallScriptFunction(f); + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + } + else + { + // Pre-allocate the memory + asDWORD *mem = (asDWORD*)m_engine->CallAlloc(objType); + + if( func ) + { + // Push the object pointer on the stack (it will be popped by the function) + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = (asPWORD)mem; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + l_sp += CallSystemFunction(func, this); + } + + // Pop the variable address from the stack + asDWORD **a = (asDWORD**)*(asPWORD*)l_sp; + l_sp += AS_PTR_SIZE; + if( a ) *a = mem; + + l_bc += 2+AS_PTR_SIZE; + + if( m_regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( m_doSuspend ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( m_status != asEXECUTION_ACTIVE ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_engine->CallFree(mem); + *a = 0; + + return; + } + } + } + } + break; + + case asBC_FREE: + { + // Get the variable that holds the object handle/reference + asPWORD *a = (asPWORD*)asPWORD(l_fp - asBC_SWORDARG0(l_bc)); + if( *a ) + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc); + asSTypeBehaviour *beh = &objType->beh; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + if( objType->flags & asOBJ_REF ) + { + asASSERT( (objType->flags & asOBJ_NOCOUNT) || beh->release ); + if( beh->release ) + m_engine->CallObjectMethod((void*)(asPWORD)*a, beh->release); + } + else + { + if( beh->destruct ) + m_engine->CallObjectMethod((void*)(asPWORD)*a, beh->destruct); + else if( objType->flags & asOBJ_LIST_PATTERN ) + m_engine->DestroyList((asBYTE*)(asPWORD)*a, objType); + + m_engine->CallFree((void*)(asPWORD)*a); + } + + // Clear the variable + *a = 0; + } + } + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_LOADOBJ: + { + // Move the object pointer from the object variable into the object register + void **a = (void**)(l_fp - asBC_SWORDARG0(l_bc)); + m_regs.objectType = 0; + m_regs.objectRegister = *a; + *a = 0; + } + l_bc++; + break; + + case asBC_STOREOBJ: + // Move the object pointer from the object register to the object variable + *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asPWORD(m_regs.objectRegister); + m_regs.objectRegister = 0; + l_bc++; + break; + + case asBC_GETOBJ: + { + // Read variable index from location on stack + asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc)); + asPWORD offset = *a; + // Move pointer from variable to the same location on the stack + asPWORD *v = (asPWORD*)(l_fp - offset); + *a = *v; + // Clear variable + *v = 0; + } + l_bc++; + break; + + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc); + asSTypeBehaviour *beh = &objType->beh; + + // Pop address of destination pointer from the stack + void **d = (void**)*(asPWORD*)l_sp; + l_sp += AS_PTR_SIZE; + + // Read wanted pointer from the stack + void *s = (void*)*(asPWORD*)l_sp; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + if( !(objType->flags & asOBJ_NOCOUNT) ) + { + // Release previous object held by destination pointer + if( *d != 0 ) + m_engine->CallObjectMethod(*d, beh->release); + // Increase ref counter of wanted object + if( s != 0 ) + m_engine->CallObjectMethod(s, beh->addref); + } + + // Set the new object in the destination + *d = s; + } + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_CHKREF: + { + // Verify if the pointer on the stack is null + // This is used when validating a pointer that an operator will work on + asPWORD a = *(asPWORD*)l_sp; + if( a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_GETOBJREF: + { + // Get the location on the stack where the reference will be placed + asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc)); + + // Replace the variable index with the object handle held in the variable + *(asPWORD**)a = *(asPWORD**)(l_fp - *a); + } + l_bc++; + break; + + case asBC_GETREF: + { + // Get the location on the stack where the reference will be placed + asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc)); + + // Replace the variable index with the address of the variable + *(asPWORD**)a = (asPWORD*)(l_fp - (int)*a); + } + l_bc++; + break; + + case asBC_PshNull: + // Push a null pointer on the stack + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = 0; + l_bc++; + break; + + case asBC_ClrVPtr: + // TODO: runtime optimize: Is this instruction really necessary? + // CallScriptFunction() can clear the null handles upon entry, just as is done for + // all other object variables + // Clear pointer variable + *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = 0; + l_bc++; + break; + + case asBC_OBJTYPE: + // Push the object type on the stack + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_TYPEID: + // Equivalent to PshC4, but kept as separate instruction for bytecode serialization + --l_sp; + *l_sp = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV4: + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asBC_QWORDARG(l_bc); + l_bc += 3; + break; + + case asBC_ADDSi: + { + // The pointer must not be null + asPWORD a = *(asPWORD*)l_sp; + if( a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + // Add an offset to the pointer + *(asPWORD*)l_sp = a + asBC_SWORDARG0(l_bc); + } + l_bc += 2; + break; + + case asBC_CpyVtoV4: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)); + l_bc += 2; + break; + + case asBC_CpyVtoV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + l_bc += 2; + break; + + case asBC_CpyVtoR4: + *(asDWORD*)&m_regs.valueRegister = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_CpyVtoR8: + *(asQWORD*)&m_regs.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_CpyVtoG4: + *(asDWORD*)asBC_PTRARG(l_bc) = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc += 1 + AS_PTR_SIZE; + break; + + case asBC_CpyRtoV4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)&m_regs.valueRegister; + l_bc++; + break; + + case asBC_CpyRtoV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = m_regs.valueRegister; + l_bc++; + break; + + case asBC_CpyGtoV4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)asBC_PTRARG(l_bc); + l_bc += 1 + AS_PTR_SIZE; + break; + + case asBC_WRTV1: + // The pointer in the register points to a byte, and *(l_fp - offset) too + **(asBYTE**)&m_regs.valueRegister = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV2: + // The pointer in the register points to a word, and *(l_fp - offset) too + **(asWORD**)&m_regs.valueRegister = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV4: + **(asDWORD**)&m_regs.valueRegister = *(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV8: + **(asQWORD**)&m_regs.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_RDR1: + { + // The pointer in the register points to a byte, and *(l_fp - offset) will also point to a byte + asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + bPtr[0] = **(asBYTE**)&m_regs.valueRegister; // read the byte + bPtr[1] = 0; // 0 the rest of the DWORD + bPtr[2] = 0; + bPtr[3] = 0; + } + l_bc++; + break; + + case asBC_RDR2: + { + // The pointer in the register points to a word, and *(l_fp - offset) will also point to a word + asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + wPtr[0] = **(asWORD**)&m_regs.valueRegister; // read the word + wPtr[1] = 0; // 0 the rest of the DWORD + } + l_bc++; + break; + + case asBC_RDR4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)&m_regs.valueRegister; + l_bc++; + break; + + case asBC_RDR8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asQWORD**)&m_regs.valueRegister; + l_bc++; + break; + + case asBC_LDG: + *(asPWORD*)&m_regs.valueRegister = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_LDV: + *(asDWORD**)&m_regs.valueRegister = (l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_PGA: + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_CmpPtr: + { + // TODO: runtime optimize: This instruction should really just be an equals, and return true or false. + // The instruction is only used for is and !is tests anyway. + asPWORD p1 = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asPWORD p2 = *(asPWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + if( p1 == p2 ) *(int*)&m_regs.valueRegister = 0; + else if( p1 < p2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_VAR: + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = (asPWORD)asBC_SWORDARG0(l_bc); + l_bc++; + break; + + //---------------------------- + // Type conversions + case asBC_iTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(int*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(float*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_uTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOu: + // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0 + *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(float*)(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + case asBC_sbTOi: + // *(l_fp - offset) points to a char, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(signed char*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_swTOi: + // *(l_fp - offset) points to a short, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(short*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_ubTOi: + // (l_fp - offset) points to a byte, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_uwTOi: + // *(l_fp - offset) points to a word, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_dTOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(double*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_dTOu: + // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0 + *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(double*)(l_fp - asBC_SWORDARG1(l_bc)))); + l_bc += 2; + break; + + case asBC_dTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(double*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_iTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(int*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_uTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_fTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(float*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + //------------------------------ + // Math operations + case asBC_ADDi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVi: + { + int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + else if( divider == -1 ) + { + // Need to check if the value that is divided is 0x80000000 + // as dividing it with -1 will cause an overflow exception + if( *(int*)(l_fp - asBC_SWORDARG1(l_bc)) == int(0x80000000) ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_OVERFLOW); + return; + } + } + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODi: + { + int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + else if( divider == -1 ) + { + // Need to check if the value that is divided is 0x80000000 + // as dividing it with -1 will cause an overflow exception + if( *(int*)(l_fp - asBC_SWORDARG1(l_bc)) == int(0x80000000) ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_OVERFLOW); + return; + } + } + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_ADDf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVf: + { + float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODf: + { + float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = fmodf(*(float*)(l_fp - asBC_SWORDARG1(l_bc)), divider); + } + l_bc += 2; + break; + + case asBC_ADDd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) + *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) - *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) * *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVd: + { + double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + l_bc += 2; + } + break; + + case asBC_MODd: + { + double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = fmod(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), divider); + l_bc += 2; + } + break; + + //------------------------------ + // Math operations with constant value + case asBC_ADDIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_SUBIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_MULIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_ADDIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + case asBC_SUBIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + case asBC_MULIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + //----------------------------------- + case asBC_SetG4: + *(asDWORD*)asBC_PTRARG(l_bc) = asBC_DWORDARG(l_bc+AS_PTR_SIZE); + l_bc += 2 + AS_PTR_SIZE; + break; + + case asBC_ChkRefS: + { + // Verify if the pointer on the stack refers to a non-null value + // This is used to validate a reference to a handle + asPWORD *a = (asPWORD*)*(asPWORD*)l_sp; + if( *a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_ChkNullV: + { + // Verify if variable (on the stack) is not null + asDWORD *a = *(asDWORD**)(l_fp - asBC_SWORDARG0(l_bc)); + if( a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_CALLINTF: + { + int i = asBC_INTARG(l_bc); + l_bc += 2; + + asASSERT( i >= 0 ); + asASSERT( (i & FUNC_IMPORTED) == 0 ); + + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + CallInterfaceMethod(m_engine->GetScriptFunction(i)); + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + } + break; + + case asBC_iTOb: + { + // *(l_fp - offset) points to an int, and will point to a byte afterwards + + // We need to use volatile here to tell the compiler not to rearrange + // read and write operations during optimizations. + volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc)); + volatile asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + bPtr[0] = (asBYTE)val; // write the byte + bPtr[1] = 0; // 0 the rest of the DWORD + bPtr[2] = 0; + bPtr[3] = 0; + } + l_bc++; + break; + + case asBC_iTOw: + { + // *(l_fp - offset) points to an int, and will point to word afterwards + + // We need to use volatile here to tell the compiler not to rearrange + // read and write operations during optimizations. + volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc)); + volatile asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + wPtr[0] = (asWORD)val; // write the word + wPtr[1] = 0; // 0 the rest of the DWORD + } + l_bc++; + break; + + case asBC_SetV1: + // TODO: This is exactly the same as SetV4. This is a left over from the time + // when the bytecode instructions were more tightly packed. It can now + // be removed. When removing it, make sure the value is correctly converted + // on big-endian CPUs. + + // The byte is already stored correctly in the argument + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV2: + // TODO: This is exactly the same as SetV4. This is a left over from the time + // when the bytecode instructions were more tightly packed. It can now + // be removed. When removing it, make sure the value is correctly converted + // on big-endian CPUs. + + // The word is already stored correctly in the argument + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_Cast: + // Cast the handle at the top of the stack to the type in the argument + { + asDWORD **a = (asDWORD**)*(asPWORD*)l_sp; + if( a && *a ) + { + asDWORD typeId = asBC_DWORDARG(l_bc); + + asCScriptObject *obj = (asCScriptObject *)* a; + asCObjectType *objType = obj->objType; + asCObjectType *to = m_engine->GetObjectTypeFromTypeId(typeId); + + // This instruction can only be used with script classes and interfaces + asASSERT( objType->flags & asOBJ_SCRIPT_OBJECT ); + asASSERT( to->flags & asOBJ_SCRIPT_OBJECT ); + + if( objType->Implements(to) || objType->DerivesFrom(to) ) + { + m_regs.objectType = 0; + m_regs.objectRegister = obj; + obj->AddRef(); + } + else + { + // The object register should already be null, so there + // is no need to clear it if the cast is unsuccessful + asASSERT( m_regs.objectRegister == 0 ); + } + } + l_sp += AS_PTR_SIZE; + } + l_bc += 2; + break; + + case asBC_i64TOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_uTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_iTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(int*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_fTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_dTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOu64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc)))); + l_bc += 2; + break; + + case asBC_dTOu64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + case asBC_i64TOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_u64TOf: +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6 + { + // MSVC6 doesn't permit UINT64 to double + asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)); + if( v < 0 ) + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0f+float(v); + else + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(v); + } +#else + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc))); +#endif + l_bc += 2; + break; + + case asBC_i64TOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_u64TOd: +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6 + { + // MSVC6 doesn't permit UINT64 to double + asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); + if( v < 0 ) + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0+double(v); + else + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(v); + } +#else + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc))); +#endif + l_bc++; + break; + + case asBC_NEGi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_INCi64: + ++(**(asQWORD**)&m_regs.valueRegister); + l_bc++; + break; + + case asBC_DECi64: + --(**(asQWORD**)&m_regs.valueRegister); + l_bc++; + break; + + case asBC_BNOT64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = ~*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_ADDi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) + *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) - *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) * *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVi64: + { + asINT64 divider = *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + else if( divider == -1 ) + { + // Need to check if the value that is divided is 1<<63 + // as dividing it with -1 will cause an overflow exception + if( *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) == (asINT64(1)<<63) ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_OVERFLOW); + return; + } + } + + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODi64: + { + asINT64 divider = *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + else if( divider == -1 ) + { + // Need to check if the value that is divided is 1<<63 + // as dividing it with -1 will cause an overflow exception + if( *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) == (asINT64(1)<<63) ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_OVERFLOW); + return; + } + } + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_BAND64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) & *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BOR64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) | *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BXOR64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) ^ *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSLL64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSRL64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSRA64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_CMPi64: + { + asINT64 i1 = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); + asINT64 i2 = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)); + if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0; + else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPu64: + { + asQWORD d1 = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asQWORD d2 = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0; + else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1; + else *(int*)&m_regs.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_ChkNullS: + { + // Verify if the pointer on the stack is null + // This is used for example when validating handles passed as function arguments + asPWORD a = *(asPWORD*)(l_sp + asBC_WORDARG0(l_bc)); + if( a == 0 ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_ClrHi: +#if AS_SIZEOF_BOOL == 1 + { + // Clear the upper bytes, so that trash data don't interfere with boolean operations + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on the pointer. + + volatile asBYTE *ptr = (asBYTE*)&m_regs.valueRegister; + ptr[1] = 0; // The boolean value is stored in the lower byte, so we clear the rest + ptr[2] = 0; + ptr[3] = 0; + } +#else + // We don't have anything to do here +#endif + l_bc++; + break; + + case asBC_JitEntry: + { + if( m_currentFunction->scriptData->jitFunction ) + { + asPWORD jitArg = asBC_PTRARG(l_bc); + + if( jitArg ) + { + // Resume JIT operation + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + (m_currentFunction->scriptData->jitFunction)(&m_regs, jitArg); + + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + + break; + } + } + + // Not a JIT resume point, treat as nop + l_bc += 1+AS_PTR_SIZE; + } + break; + + case asBC_CallPtr: + { + // Get the function pointer from the local variable + asCScriptFunction *func = *(asCScriptFunction**)(l_fp - asBC_SWORDARG0(l_bc)); + + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + if( func == 0 ) + { + // Need to update the program pointer anyway for the exception handler + m_regs.programPointer++; + + // Tell the exception handler to clean up the arguments to this method + m_needToCleanupArgs = true; + + // TODO: funcdef: Should we have a different exception string? + SetInternalException(TXT_UNBOUND_FUNCTION); + return; + } + else + { + if( func->funcType == asFUNC_SCRIPT ) + { + m_regs.programPointer++; + CallScriptFunction(func); + } + else if( func->funcType == asFUNC_DELEGATE ) + { + // Push the object pointer on the stack. There is always a reserved space for this so + // we don't don't need to worry about overflowing the allocated memory buffer + asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] ); + m_regs.stackPointer -= AS_PTR_SIZE; + *(asPWORD*)m_regs.stackPointer = asPWORD(func->objForDelegate); + + // Call the delegated method + if( func->funcForDelegate->funcType == asFUNC_SYSTEM ) + { + m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer++; + } + else + { + m_regs.programPointer++; + + // TODO: run-time optimize: The true method could be figured out when creating the delegate + CallInterfaceMethod(func->funcForDelegate); + } + } + else + { + asASSERT( func->funcType == asFUNC_SYSTEM ); + + m_regs.stackPointer += CallSystemFunction(func->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer++; + } + } + + // Extract the values from the context again + l_bc = m_regs.programPointer; + l_sp = m_regs.stackPointer; + l_fp = m_regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( m_status != asEXECUTION_ACTIVE ) + return; + } + break; + + case asBC_FuncPtr: + // Push the function pointer on the stack. The pointer is in the argument + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_LoadThisR: + { + // PshVPtr 0 + asPWORD tmp = *(asPWORD*)l_fp; + + // Make sure the pointer is not null + if( tmp == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + + // ADDSi + tmp = tmp + asBC_SWORDARG0(l_bc); + + // PopRPtr + *(asPWORD*)&m_regs.valueRegister = tmp; + l_bc += 2; + } + break; + + // Push the qword value of a variable on the stack + case asBC_PshV8: + l_sp -= 2; + *(asQWORD*)l_sp = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_DIVu: + { + asUINT divider = *(asUINT*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asUINT*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODu: + { + asUINT divider = *(asUINT*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asUINT*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_DIVu64: + { + asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODu64: + { + asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_LoadRObjR: + { + // PshVPtr x + asPWORD tmp = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + + // Make sure the pointer is not null + if( tmp == 0 ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + + // ADDSi y + tmp = tmp + asBC_SWORDARG1(l_bc); + + // PopRPtr + *(asPWORD*)&m_regs.valueRegister = tmp; + l_bc += 3; + } + break; + + case asBC_LoadVObjR: + { + // PSF x + asPWORD tmp = (asPWORD)(l_fp - asBC_SWORDARG0(l_bc)); + + // ADDSi y + tmp = tmp + asBC_SWORDARG1(l_bc); + + // PopRPtr + *(asPWORD*)&m_regs.valueRegister = tmp; + l_bc += 3; + } + break; + + case asBC_RefCpyV: + // Same as PSF v, REFCPY + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc); + asSTypeBehaviour *beh = &objType->beh; + + // Determine destination from argument + void **d = (void**)asPWORD(l_fp - asBC_SWORDARG0(l_bc)); + + // Read wanted pointer from the stack + void *s = (void*)*(asPWORD*)l_sp; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + if( !(objType->flags & asOBJ_NOCOUNT) ) + { + // Release previous object held by destination pointer + if( *d != 0 ) + m_engine->CallObjectMethod(*d, beh->release); + // Increase ref counter of wanted object + if( s != 0 ) + m_engine->CallObjectMethod(s, beh->addref); + } + + // Set the new object in the destination + *d = s; + } + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_JLowZ: + if( *(asBYTE*)&m_regs.valueRegister == 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + case asBC_JLowNZ: + if( *(asBYTE*)&m_regs.valueRegister != 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + case asBC_AllocMem: + // Allocate a buffer and store the pointer in the local variable + { + // TODO: runtime optimize: As the list buffers are going to be short lived, it may be interesting + // to use a memory pool to avoid reallocating the memory all the time + + asUINT size = asBC_DWORDARG(l_bc); + asBYTE **var = (asBYTE**)(l_fp - asBC_SWORDARG0(l_bc)); +#ifndef WIP_16BYTE_ALIGN + *var = asNEWARRAY(asBYTE, size); +#else + *var = asNEWARRAYALIGNED(asBYTE, size, MAX_TYPE_ALIGNMENT); +#endif + + // Clear the buffer for the pointers that will be placed in it + memset(*var, 0, size); + } + l_bc += 2; + break; + + case asBC_SetListSize: + { + // Set the size element in the buffer + asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc)); + asUINT off = asBC_DWORDARG(l_bc); + asUINT size = asBC_DWORDARG(l_bc+1); + + asASSERT( var ); + + *(asUINT*)(var+off) = size; + } + l_bc += 3; + break; + + case asBC_PshListElmnt: + { + // Push the pointer to the list element on the stack + // In essence it does the same as PSF, RDSPtr, ADDSi + asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc)); + asUINT off = asBC_DWORDARG(l_bc); + + asASSERT( var ); + + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = asPWORD(var+off); + } + l_bc += 2; + break; + + case asBC_SetListType: + { + // Set the type id in the buffer + asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc)); + asUINT off = asBC_DWORDARG(l_bc); + asUINT type = asBC_DWORDARG(l_bc+1); + + asASSERT( var ); + + *(asUINT*)(var+off) = type; + } + l_bc += 3; + break; + + //------------------------------ + // Exponent operations + case asBC_POWi: + { + bool isOverflow; + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powi(*(int*)(l_fp - asBC_SWORDARG1(l_bc)), *(int*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow); + if( isOverflow ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + + case asBC_POWu: + { + bool isOverflow; + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powu(*(asDWORD*)(l_fp - asBC_SWORDARG1(l_bc)), *(asDWORD*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow); + if( isOverflow ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + + case asBC_POWf: + { + float r = powf(*(float*)(l_fp - asBC_SWORDARG1(l_bc)), *(float*)(l_fp - asBC_SWORDARG2(l_bc))); + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = r; + if( r == float(HUGE_VAL) ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + + case asBC_POWd: + { + double r = pow(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), *(double*)(l_fp - asBC_SWORDARG2(l_bc))); + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = r; + if( r == HUGE_VAL ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + + case asBC_POWdi: + { + double r = pow(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), *(int*)(l_fp - asBC_SWORDARG2(l_bc))); + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = r; + if( r == HUGE_VAL ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + l_bc += 2; + } + break; + + case asBC_POWi64: + { + bool isOverflow; + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powi64(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)), *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow); + if( isOverflow ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + + case asBC_POWu64: + { + bool isOverflow; + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powu64(*(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)), *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow); + if( isOverflow ) + { + // Need to move the values back to the context + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_POW_OVERFLOW); + return; + } + } + l_bc += 2; + break; + case asBC_Thiscall1: + // This instruction is a faster version of asBC_CALLSYS. It is faster because + // it has much less runtime overhead with determining the calling convention + // and no dynamic code for loading the parameters. The instruction can only + // be used to call functions with the following signatures: + // + // type &obj::func(int) + // type &obj::func(uint) + // void obj::func(int) + // void obj::func(uint) + { + // Get function ID from the argument + int i = asBC_INTARG(l_bc); + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + // Pop the thispointer from the stack + void *obj = *(void**)l_sp; + if (obj == 0) + SetInternalException(TXT_NULL_POINTER_ACCESS); + else + { + // Only update the stack pointer if all is OK so the + // exception handler can properly clean up the stack + l_sp += AS_PTR_SIZE; + + // Pop the int arg from the stack + int arg = *(int*)l_sp; + l_sp++; + + // Call the method + m_callingSystemFunction = m_engine->scriptFunctions[i]; + void *ptr = m_engine->CallObjectMethodRetPtr(obj, arg, m_callingSystemFunction); + m_callingSystemFunction = 0; + *(asPWORD*)&m_regs.valueRegister = (asPWORD)ptr; + } + + // Update the program position after the call so that line number is correct + l_bc += 2; + + if( m_regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( m_doSuspend ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( m_status != asEXECUTION_ACTIVE ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + return; + } + } + } + break; + + // Don't let the optimizer optimize for size, + // since it requires extra conditions and jumps + case 201: l_bc = (asDWORD*)201; break; + case 202: l_bc = (asDWORD*)202; break; + case 203: l_bc = (asDWORD*)203; break; + case 204: l_bc = (asDWORD*)204; break; + case 205: l_bc = (asDWORD*)205; break; + case 206: l_bc = (asDWORD*)206; break; + case 207: l_bc = (asDWORD*)207; break; + case 208: l_bc = (asDWORD*)208; break; + case 209: l_bc = (asDWORD*)209; break; + case 210: l_bc = (asDWORD*)210; break; + case 211: l_bc = (asDWORD*)211; break; + case 212: l_bc = (asDWORD*)212; break; + case 213: l_bc = (asDWORD*)213; break; + case 214: l_bc = (asDWORD*)214; break; + case 215: l_bc = (asDWORD*)215; break; + case 216: l_bc = (asDWORD*)216; break; + case 217: l_bc = (asDWORD*)217; break; + case 218: l_bc = (asDWORD*)218; break; + case 219: l_bc = (asDWORD*)219; break; + case 220: l_bc = (asDWORD*)220; break; + case 221: l_bc = (asDWORD*)221; break; + case 222: l_bc = (asDWORD*)222; break; + case 223: l_bc = (asDWORD*)223; break; + case 224: l_bc = (asDWORD*)224; break; + case 225: l_bc = (asDWORD*)225; break; + case 226: l_bc = (asDWORD*)226; break; + case 227: l_bc = (asDWORD*)227; break; + case 228: l_bc = (asDWORD*)228; break; + case 229: l_bc = (asDWORD*)229; break; + case 230: l_bc = (asDWORD*)230; break; + case 231: l_bc = (asDWORD*)231; break; + case 232: l_bc = (asDWORD*)232; break; + case 233: l_bc = (asDWORD*)233; break; + case 234: l_bc = (asDWORD*)234; break; + case 235: l_bc = (asDWORD*)235; break; + case 236: l_bc = (asDWORD*)236; break; + case 237: l_bc = (asDWORD*)237; break; + case 238: l_bc = (asDWORD*)238; break; + case 239: l_bc = (asDWORD*)239; break; + case 240: l_bc = (asDWORD*)240; break; + case 241: l_bc = (asDWORD*)241; break; + case 242: l_bc = (asDWORD*)242; break; + case 243: l_bc = (asDWORD*)243; break; + case 244: l_bc = (asDWORD*)244; break; + case 245: l_bc = (asDWORD*)245; break; + case 246: l_bc = (asDWORD*)246; break; + case 247: l_bc = (asDWORD*)247; break; + case 248: l_bc = (asDWORD*)248; break; + case 249: l_bc = (asDWORD*)249; break; + case 250: l_bc = (asDWORD*)250; break; + case 251: l_bc = (asDWORD*)251; break; + case 252: l_bc = (asDWORD*)252; break; + case 253: l_bc = (asDWORD*)253; break; + case 254: l_bc = (asDWORD*)254; break; + case 255: l_bc = (asDWORD*)255; break; + +#ifdef AS_DEBUG + default: + asASSERT(false); + SetInternalException(TXT_UNRECOGNIZED_BYTE_CODE); +#endif +#if defined(_MSC_VER) && !defined(AS_DEBUG) + default: + // This Microsoft specific code allows the + // compiler to optimize the switch case as + // it will know that the code will never + // reach this point + __assume(0); +#endif + } + +#ifdef AS_DEBUG + asDWORD instr = *(asBYTE*)old; + if( instr != asBC_JMP && instr != asBC_JMPP && (instr < asBC_JZ || instr > asBC_JNP) && instr != asBC_JLowZ && instr != asBC_JLowNZ && + instr != asBC_CALL && instr != asBC_CALLBND && instr != asBC_CALLINTF && instr != asBC_RET && instr != asBC_ALLOC && instr != asBC_CallPtr && + instr != asBC_JitEntry ) + { + asASSERT( (l_bc - old) == asBCTypeSize[asBCInfo[instr].type] ); + } +#endif + } +} + +int asCContext::SetException(const char *descr) +{ + // Only allow this if we're executing a CALL byte code + if( m_callingSystemFunction == 0 ) return asERROR; + + SetInternalException(descr); + + return 0; +} + +void asCContext::SetInternalException(const char *descr) +{ + if( m_inExceptionHandler ) + { + asASSERT(false); // Shouldn't happen + return; // but if it does, at least this will not crash the application + } + + m_status = asEXECUTION_EXCEPTION; + m_regs.doProcessSuspend = true; + + m_exceptionString = descr; + m_exceptionFunction = m_currentFunction->id; + + if( m_currentFunction->scriptData ) + { + m_exceptionLine = m_currentFunction->GetLineNumber(int(m_regs.programPointer - m_currentFunction->scriptData->byteCode.AddressOf()), &m_exceptionSectionIdx); + m_exceptionColumn = m_exceptionLine >> 20; + m_exceptionLine &= 0xFFFFF; + } + else + { + m_exceptionSectionIdx = 0; + m_exceptionLine = 0; + m_exceptionColumn = 0; + } + + if( m_exceptionCallback ) + CallExceptionCallback(); +} + +void asCContext::CleanReturnObject() +{ + if( m_initialFunction && m_initialFunction->DoesReturnOnStack() && m_status == asEXECUTION_FINISHED ) + { + // If function returns on stack we need to call the destructor on the returned object + if(CastToObjectType(m_initialFunction->returnType.GetTypeInfo())->beh.destruct ) + m_engine->CallObjectMethod(GetReturnObject(), CastToObjectType(m_initialFunction->returnType.GetTypeInfo())->beh.destruct); + + return; + } + + if( m_regs.objectRegister == 0 ) return; + + asASSERT( m_regs.objectType != 0 ); + + if( m_regs.objectType ) + { + if (m_regs.objectType->GetFlags() & asOBJ_FUNCDEF) + { + // Release the function pointer + reinterpret_cast(m_regs.objectRegister)->Release(); + m_regs.objectRegister = 0; + } + else + { + // Call the destructor on the object + asSTypeBehaviour *beh = &(CastToObjectType(reinterpret_cast(m_regs.objectType))->beh); + if (m_regs.objectType->GetFlags() & asOBJ_REF) + { + asASSERT(beh->release || (m_regs.objectType->GetFlags() & asOBJ_NOCOUNT)); + + if (beh->release) + m_engine->CallObjectMethod(m_regs.objectRegister, beh->release); + + m_regs.objectRegister = 0; + } + else + { + if (beh->destruct) + m_engine->CallObjectMethod(m_regs.objectRegister, beh->destruct); + + // Free the memory + m_engine->CallFree(m_regs.objectRegister); + m_regs.objectRegister = 0; + } + } + } +} + +void asCContext::CleanStack() +{ + m_inExceptionHandler = true; + + // Run the clean up code for each of the functions called + CleanStackFrame(); + + // Set the status to exception so that the stack unwind is done correctly. + // This shouldn't be done for the current function, which is why we only + // do this after the first CleanStackFrame() is done. + m_status = asEXECUTION_EXCEPTION; + + while( m_callStack.GetLength() > 0 ) + { + // Only clean up until the top most marker for a nested call + asPWORD *s = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE; + if( s[0] == 0 ) + break; + + PopCallState(); + + CleanStackFrame(); + } + + m_inExceptionHandler = false; +} + +// Interface +bool asCContext::IsVarInScope(asUINT varIndex, asUINT stackLevel) +{ + // Don't return anything if there is no bytecode, e.g. before calling Execute() + if( m_regs.programPointer == 0 ) return false; + + if( stackLevel >= GetCallstackSize() ) return false; + + asCScriptFunction *func; + asUINT pos; + + if( stackLevel == 0 ) + { + func = m_currentFunction; + if( func->scriptData == 0 ) return false; + pos = asUINT(m_regs.programPointer - func->scriptData->byteCode.AddressOf()); + } + else + { + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + if( func->scriptData == 0 ) return false; + pos = asUINT((asDWORD*)s[2] - func->scriptData->byteCode.AddressOf()); + } + + // First determine if the program position is after the variable declaration + if( func->scriptData->variables.GetLength() <= varIndex ) return false; + if( func->scriptData->variables[varIndex]->declaredAtProgramPos > pos ) return false; + + asUINT declaredAt = func->scriptData->variables[varIndex]->declaredAtProgramPos; + + // If the program position is after the variable declaration it is necessary + // determine if the program position is still inside the statement block where + // the variable was delcared. + for( int n = 0; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ ) + { + if( func->scriptData->objVariableInfo[n].programPos >= declaredAt ) + { + // If the current block ends between the declaredAt and current + // program position, then we know the variable is no longer visible + int level = 0; + for( ; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ ) + { + if( func->scriptData->objVariableInfo[n].programPos > pos ) + break; + + if( func->scriptData->objVariableInfo[n].option == asBLOCK_BEGIN ) level++; + if( func->scriptData->objVariableInfo[n].option == asBLOCK_END && --level < 0 ) + return false; + } + + break; + } + } + + // Variable is visible + return true; +} + +// Internal +void asCContext::DetermineLiveObjects(asCArray &liveObjects, asUINT stackLevel) +{ + asASSERT( stackLevel < GetCallstackSize() ); + + asCScriptFunction *func; + asUINT pos; + + if( stackLevel == 0 ) + { + func = m_currentFunction; + if( func->scriptData == 0 ) + return; + + pos = asUINT(m_regs.programPointer - func->scriptData->byteCode.AddressOf()); + + if( m_status == asEXECUTION_EXCEPTION ) + { + // Don't consider the last instruction as executed, as it failed with an exception + // It's not actually necessary to decrease the exact size of the instruction. Just + // before the current position is enough to disconsider it. + pos--; + } + } + else + { + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + if( func->scriptData == 0 ) + return; + + pos = asUINT((asDWORD*)s[2] - func->scriptData->byteCode.AddressOf()); + + // Don't consider the last instruction as executed, as the function that was called by it + // is still being executed. If we consider it as executed already, then a value object + // returned by value would be considered alive, which it is not. + pos--; + } + + // Determine which object variables that are really live ones + liveObjects.SetLength(func->scriptData->objVariablePos.GetLength()); + memset(liveObjects.AddressOf(), 0, sizeof(int)*liveObjects.GetLength()); + for( int n = 0; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ ) + { + // Find the first variable info with a larger position than the current + // As the variable info are always placed on the instruction right after the + // one that initialized or freed the object, the current position needs to be + // considered as valid. + if( func->scriptData->objVariableInfo[n].programPos > pos ) + { + // We've determined how far the execution ran, now determine which variables are alive + for( --n; n >= 0; n-- ) + { + switch( func->scriptData->objVariableInfo[n].option ) + { + case asOBJ_UNINIT: // Object was destroyed + { + // TODO: optimize: This should have been done by the compiler already + // Which variable is this? + asUINT var = 0; + for( asUINT v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ ) + if( func->scriptData->objVariablePos[v] == func->scriptData->objVariableInfo[n].variableOffset ) + { + var = v; + break; + } + liveObjects[var] -= 1; + } + break; + case asOBJ_INIT: // Object was created + { + // Which variable is this? + asUINT var = 0; + for( asUINT v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ ) + if( func->scriptData->objVariablePos[v] == func->scriptData->objVariableInfo[n].variableOffset ) + { + var = v; + break; + } + liveObjects[var] += 1; + } + break; + case asBLOCK_BEGIN: // Start block + // We should ignore start blocks, since it just means the + // program was within the block when the exception ocurred + break; + case asBLOCK_END: // End block + // We need to skip the entire block, as the objects created + // and destroyed inside this block are already out of scope + { + int nested = 1; + while( nested > 0 ) + { + int option = func->scriptData->objVariableInfo[--n].option; + if( option == 3 ) + nested++; + if( option == 2 ) + nested--; + } + } + break; + } + } + + // We're done with the investigation + break; + } + } +} + +void asCContext::CleanArgsOnStack() +{ + if( !m_needToCleanupArgs ) + return; + + asASSERT( m_currentFunction->scriptData ); + + // Find the instruction just before the current program pointer + asDWORD *instr = m_currentFunction->scriptData->byteCode.AddressOf(); + asDWORD *prevInstr = 0; + while( instr < m_regs.programPointer ) + { + prevInstr = instr; + instr += asBCTypeSize[asBCInfo[*(asBYTE*)(instr)].type]; + } + + // Determine what function was being called + asCScriptFunction *func = 0; + asBYTE bc = *(asBYTE*)prevInstr; + if( bc == asBC_CALL || bc == asBC_CALLSYS || bc == asBC_CALLINTF ) + { + int funcId = asBC_INTARG(prevInstr); + func = m_engine->scriptFunctions[funcId]; + } + else if( bc == asBC_CALLBND ) + { + int funcId = asBC_INTARG(prevInstr); + func = m_engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature; + } + else if( bc == asBC_CallPtr ) + { + asUINT v; + int var = asBC_SWORDARG0(prevInstr); + + // Find the funcdef from the local variable + for( v = 0; v < m_currentFunction->scriptData->objVariablePos.GetLength(); v++ ) + if( m_currentFunction->scriptData->objVariablePos[v] == var ) + { + func = CastToFuncdefType(m_currentFunction->scriptData->objVariableTypes[v])->funcdef; + break; + } + + if( func == 0 ) + { + // Look in parameters + int paramPos = 0; + if( m_currentFunction->objectType ) + paramPos -= AS_PTR_SIZE; + if( m_currentFunction->DoesReturnOnStack() ) + paramPos -= AS_PTR_SIZE; + for( v = 0; v < m_currentFunction->parameterTypes.GetLength(); v++ ) + { + if( var == paramPos ) + { + if (m_currentFunction->parameterTypes[v].IsFuncdef()) + func = CastToFuncdefType(m_currentFunction->parameterTypes[v].GetTypeInfo())->funcdef; + break; + } + paramPos -= m_currentFunction->parameterTypes[v].GetSizeOnStackDWords(); + } + } + } + else + asASSERT( false ); + + asASSERT( func ); + + // Clean parameters + int offset = 0; + if( func->objectType ) + offset += AS_PTR_SIZE; + if( func->DoesReturnOnStack() ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( (func->parameterTypes[n].IsObject() || func->parameterTypes[n].IsFuncdef()) && !func->parameterTypes[n].IsReference() ) + { + // TODO: cleanup: This logic is repeated twice in CleanStackFrame too. Should create a common function to share the code + if( *(asPWORD*)&m_regs.stackPointer[offset] ) + { + // Call the object's destructor + asSTypeBehaviour *beh = func->parameterTypes[n].GetBehaviour(); + if (func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_FUNCDEF) + { + (*(asCScriptFunction**)&m_regs.stackPointer[offset])->Release(); + } + else if( func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_REF ) + { + asASSERT( (func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release ); + + if( beh->release ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackPointer[offset], beh->release); + } + else + { + if( beh->destruct ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackPointer[offset], beh->destruct); + + // Free the memory + m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackPointer[offset]); + } + *(asPWORD*)&m_regs.stackPointer[offset] = 0; + } + } + + offset += func->parameterTypes[n].GetSizeOnStackDWords(); + } + + m_needToCleanupArgs = false; +} + +void asCContext::CleanStackFrame() +{ + // Clean object variables on the stack + // If the stack memory is not allocated or the program pointer + // is not set, then there is nothing to clean up on the stack frame + if( !m_isStackMemoryNotAllocated && m_regs.programPointer ) + { + // If the exception occurred while calling a function it is necessary + // to clean up the arguments that were put on the stack. + CleanArgsOnStack(); + + // Restore the stack pointer + asASSERT( m_currentFunction->scriptData ); + m_regs.stackPointer += m_currentFunction->scriptData->variableSpace; + + // Determine which object variables that are really live ones + asCArray liveObjects; + DetermineLiveObjects(liveObjects, 0); + + for( asUINT n = 0; n < m_currentFunction->scriptData->objVariablePos.GetLength(); n++ ) + { + int pos = m_currentFunction->scriptData->objVariablePos[n]; + if( n < m_currentFunction->scriptData->objVariablesOnHeap ) + { + // Check if the pointer is initialized + if( *(asPWORD*)&m_regs.stackFramePointer[-pos] ) + { + // Call the object's destructor + if (m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_FUNCDEF) + { + (*(asCScriptFunction**)&m_regs.stackFramePointer[-pos])->Release(); + } + else if( m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_REF ) + { + asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh; + asASSERT( (m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_NOCOUNT) || beh->release ); + if( beh->release ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos], beh->release); + } + else + { + asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh; + if( beh->destruct ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos], beh->destruct); + else if( m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_LIST_PATTERN ) + m_engine->DestroyList((asBYTE*)*(asPWORD*)&m_regs.stackFramePointer[-pos], CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])); + + // Free the memory + m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos]); + } + *(asPWORD*)&m_regs.stackFramePointer[-pos] = 0; + } + } + else + { + asASSERT( m_currentFunction->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE ); + + // Only destroy the object if it is truly alive + if( liveObjects[n] > 0 ) + { + asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh; + if( beh->destruct ) + m_engine->CallObjectMethod((void*)(asPWORD*)&m_regs.stackFramePointer[-pos], beh->destruct); + } + } + } + } + else + m_isStackMemoryNotAllocated = false; + + // Functions that do not own the object and parameters shouldn't do any clean up + if( m_currentFunction->dontCleanUpOnException ) + return; + + // Clean object and parameters + int offset = 0; + if( m_currentFunction->objectType ) + offset += AS_PTR_SIZE; + if( m_currentFunction->DoesReturnOnStack() ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < m_currentFunction->parameterTypes.GetLength(); n++ ) + { + if( (m_currentFunction->parameterTypes[n].IsObject() ||m_currentFunction->parameterTypes[n].IsFuncdef()) && !m_currentFunction->parameterTypes[n].IsReference() ) + { + if( *(asPWORD*)&m_regs.stackFramePointer[offset] ) + { + // Call the object's destructor + asSTypeBehaviour *beh = m_currentFunction->parameterTypes[n].GetBehaviour(); + if (m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_FUNCDEF) + { + (*(asCScriptFunction**)&m_regs.stackFramePointer[offset])->Release(); + } + else if( m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_REF ) + { + asASSERT( (m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release ); + + if( beh->release ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[offset], beh->release); + } + else + { + if( beh->destruct ) + m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[offset], beh->destruct); + + // Free the memory + m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackFramePointer[offset]); + } + *(asPWORD*)&m_regs.stackFramePointer[offset] = 0; + } + } + + offset += m_currentFunction->parameterTypes[n].GetSizeOnStackDWords(); + } +} + +// interface +int asCContext::GetExceptionLineNumber(int *column, const char **sectionName) +{ + if( GetState() != asEXECUTION_EXCEPTION ) return asERROR; + + if( column ) *column = m_exceptionColumn; + + if( sectionName ) + { + // The section index can be -1 if the exception was raised in a generated function, e.g. $fact for templates + if( m_exceptionSectionIdx >= 0 ) + *sectionName = m_engine->scriptSectionNames[m_exceptionSectionIdx]->AddressOf(); + else + *sectionName = 0; + } + + return m_exceptionLine; +} + +// interface +asIScriptFunction *asCContext::GetExceptionFunction() +{ + if( GetState() != asEXECUTION_EXCEPTION ) return 0; + + return m_engine->scriptFunctions[m_exceptionFunction]; +} + +// interface +const char *asCContext::GetExceptionString() +{ + if( GetState() != asEXECUTION_EXCEPTION ) return 0; + + return m_exceptionString.AddressOf(); +} + +// interface +asEContextState asCContext::GetState() const +{ + return m_status; +} + +// interface +int asCContext::SetLineCallback(asSFuncPtr callback, void *obj, int callConv) +{ + // First turn off the line callback to avoid a second thread + // attempting to call it while the new one is still being set + m_lineCallback = false; + + m_lineCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) + { + m_regs.doProcessSuspend = m_doSuspend; + return asNOT_SUPPORTED; + } + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + m_regs.doProcessSuspend = m_doSuspend; + return asINVALID_ARG; + } + } + + int r = DetectCallingConvention(isObj, callback, callConv, 0, &m_lineCallbackFunc); + + // Turn on the line callback after setting both the function pointer and object pointer + if( r >= 0 ) m_lineCallback = true; + + // The BC_SUSPEND instruction should be processed if either line + // callback is set or if the application has requested a suspension + m_regs.doProcessSuspend = m_doSuspend || m_lineCallback; + + return r; +} + +void asCContext::CallLineCallback() +{ + if( m_lineCallbackFunc.callConv < ICC_THISCALL ) + m_engine->CallGlobalFunction(this, m_lineCallbackObj, &m_lineCallbackFunc, 0); + else + m_engine->CallObjectMethod(m_lineCallbackObj, this, &m_lineCallbackFunc, 0); +} + +// interface +int asCContext::SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) +{ + m_exceptionCallback = true; + m_exceptionCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) + return asNOT_SUPPORTED; + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + m_exceptionCallback = false; + return asINVALID_ARG; + } + } + int r = DetectCallingConvention(isObj, callback, callConv, 0, &m_exceptionCallbackFunc); + if( r < 0 ) m_exceptionCallback = false; + return r; +} + +void asCContext::CallExceptionCallback() +{ + if( m_exceptionCallbackFunc.callConv < ICC_THISCALL ) + m_engine->CallGlobalFunction(this, m_exceptionCallbackObj, &m_exceptionCallbackFunc, 0); + else + m_engine->CallObjectMethod(m_exceptionCallbackObj, this, &m_exceptionCallbackFunc, 0); +} + +// interface +void asCContext::ClearLineCallback() +{ + m_lineCallback = false; + m_regs.doProcessSuspend = m_doSuspend; +} + +// interface +void asCContext::ClearExceptionCallback() +{ + m_exceptionCallback = false; +} + +int asCContext::CallGeneric(asCScriptFunction *descr) +{ + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + void (*func)(asIScriptGeneric*) = (void (*)(asIScriptGeneric*))sysFunc->func; + int popSize = sysFunc->paramSize; + asDWORD *args = m_regs.stackPointer; + + // Verify the object pointer if it is a class method + void *currentObject = 0; + asASSERT( sysFunc->callConv == ICC_GENERIC_FUNC || sysFunc->callConv == ICC_GENERIC_METHOD ); + if( sysFunc->callConv == ICC_GENERIC_METHOD ) + { + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + currentObject = (void*)*(asPWORD*)(args); + if( currentObject == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; + } + + asASSERT( sysFunc->baseOffset == 0 ); + + // Skip object pointer + args += AS_PTR_SIZE; + } + + if( descr->DoesReturnOnStack() ) + { + // Skip the address where the return value will be stored + args += AS_PTR_SIZE; + popSize += AS_PTR_SIZE; + } + + asCGeneric gen(m_engine, descr, currentObject, args); + + m_callingSystemFunction = descr; +#ifdef AS_NO_EXCEPTIONS + func(&gen); +#else + // This try/catch block is to catch potential exception that may + // be thrown by the registered function. + try + { + func(&gen); + } + catch (...) + { + // Convert the exception to a script exception so the VM can + // properly report the error to the application and then clean up + SetException(TXT_EXCEPTION_CAUGHT); + } +#endif + m_callingSystemFunction = 0; + + m_regs.valueRegister = gen.returnVal; + m_regs.objectRegister = gen.objectRegister; + m_regs.objectType = descr->returnType.GetTypeInfo(); + + // Clean up arguments + const asUINT cleanCount = sysFunc->cleanArgs.GetLength(); + if( cleanCount ) + { + asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); + for( asUINT n = 0; n < cleanCount; n++, clean++ ) + { + void **addr = (void**)&args[clean->off]; + if( clean->op == 0 ) + { + if( *addr != 0 ) + { + m_engine->CallObjectMethod(*addr, clean->ot->beh.release); + *addr = 0; + } + } + else + { + asASSERT( clean->op == 1 || clean->op == 2 ); + asASSERT( *addr ); + + if( clean->op == 2 ) + m_engine->CallObjectMethod(*addr, clean->ot->beh.destruct); + + m_engine->CallFree(*addr); + } + } + } + + // Return how much should be popped from the stack + return popSize; +} + +// interface +int asCContext::GetVarCount(asUINT stackLevel) +{ + asIScriptFunction *func = GetFunction(stackLevel); + if( func == 0 ) return asINVALID_ARG; + + return func->GetVarCount(); +} + +// interface +const char *asCContext::GetVarName(asUINT varIndex, asUINT stackLevel) +{ + asIScriptFunction *func = GetFunction(stackLevel); + if( func == 0 ) return 0; + + const char *name = 0; + int r = func->GetVar(varIndex, &name); + return r >= 0 ? name : 0; +} + +// interface +const char *asCContext::GetVarDeclaration(asUINT varIndex, asUINT stackLevel, bool includeNamespace) +{ + asIScriptFunction *func = GetFunction(stackLevel); + if( func == 0 ) return 0; + + return func->GetVarDecl(varIndex, includeNamespace); +} + +// interface +int asCContext::GetVarTypeId(asUINT varIndex, asUINT stackLevel) +{ + asIScriptFunction *func = GetFunction(stackLevel); + if( func == 0 ) return asINVALID_ARG; + + int typeId; + int r = func->GetVar(varIndex, 0, &typeId); + return r < 0 ? r : typeId; +} + +// interface +void *asCContext::GetAddressOfVar(asUINT varIndex, asUINT stackLevel) +{ + // Don't return anything if there is no bytecode, e.g. before calling Execute() + if( m_regs.programPointer == 0 ) return 0; + + if( stackLevel >= GetCallstackSize() ) return 0; + + asCScriptFunction *func; + asDWORD *sf; + if( stackLevel == 0 ) + { + func = m_currentFunction; + sf = m_regs.stackFramePointer; + } + else + { + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + sf = (asDWORD*)s[0]; + } + + if( func == 0 ) + return 0; + + if( func->scriptData == 0 ) + return 0; + + if( varIndex >= func->scriptData->variables.GetLength() ) + return 0; + + // For object variables it's necessary to dereference the pointer to get the address of the value + // Reference parameters must also be dereferenced to give the address of the value + int pos = func->scriptData->variables[varIndex]->stackOffset; + if( (func->scriptData->variables[varIndex]->type.IsObject() && !func->scriptData->variables[varIndex]->type.IsObjectHandle()) || (pos <= 0) ) + { + // Determine if the object is really on the heap + bool onHeap = false; + if( func->scriptData->variables[varIndex]->type.IsObject() && + !func->scriptData->variables[varIndex]->type.IsObjectHandle() ) + { + onHeap = true; + if( func->scriptData->variables[varIndex]->type.GetTypeInfo()->GetFlags() & asOBJ_VALUE ) + { + for( asUINT n = 0; n < func->scriptData->objVariablePos.GetLength(); n++ ) + { + if( func->scriptData->objVariablePos[n] == pos ) + { + onHeap = n < func->scriptData->objVariablesOnHeap; + + if( !onHeap ) + { + // If the object on the stack is not initialized return a null pointer instead + asCArray liveObjects; + DetermineLiveObjects(liveObjects, stackLevel); + + if( liveObjects[n] <= 0 ) + return 0; + } + + break; + } + } + } + } + + // If it wasn't an object on the heap, then check if it is a reference parameter + if( !onHeap && pos <= 0 ) + { + // Determine what function argument this position matches + int stackPos = 0; + if( func->objectType ) + stackPos -= AS_PTR_SIZE; + + if( func->DoesReturnOnStack() ) + stackPos -= AS_PTR_SIZE; + + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( stackPos == pos ) + { + // The right argument was found. Is this a reference parameter? + if( func->inOutFlags[n] != asTM_NONE ) + onHeap = true; + + break; + } + stackPos -= func->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + if( onHeap ) + return *(void**)(sf - func->scriptData->variables[varIndex]->stackOffset); + } + + return sf - func->scriptData->variables[varIndex]->stackOffset; +} + +// interface +// returns the typeId of the 'this' object at the given call stack level (-1 for current) +// returns 0 if the function call at the given stack level is not a method +int asCContext::GetThisTypeId(asUINT stackLevel) +{ + asIScriptFunction *func = GetFunction(stackLevel); + if( func == 0 ) return asINVALID_ARG; + + if( func->GetObjectType() == 0 ) + return 0; // not in a method + + // create a datatype + asCDataType dt = asCDataType::CreateType((asCObjectType*)func->GetObjectType(), false); + + // return a typeId from the data type + return m_engine->GetTypeIdFromDataType(dt); +} + +// interface +// returns the 'this' object pointer at the given call stack level (-1 for current) +// returns 0 if the function call at the given stack level is not a method +void *asCContext::GetThisPointer(asUINT stackLevel) +{ + if( stackLevel >= GetCallstackSize() ) + return 0; + + asCScriptFunction *func; + asDWORD *sf; + if( stackLevel == 0 ) + { + func = m_currentFunction; + sf = m_regs.stackFramePointer; + } + else + { + asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + sf = (asDWORD*)s[0]; + } + + if( func == 0 ) + return 0; + + if( func->objectType == 0 ) + return 0; // not in a method + + void *thisPointer = (void*)*(asPWORD*)(sf); + if( thisPointer == 0 ) + { + return 0; + } + + // NOTE: this returns the pointer to the 'this' while the GetVarPointer functions return + // a pointer to a pointer. I can't imagine someone would want to change the 'this' + return thisPointer; +} + + + + + + + +// TODO: Move these to as_utils.cpp + +struct POW_INFO +{ + asQWORD MaxBaseu64; + asDWORD MaxBasei64; + asWORD MaxBaseu32; + asWORD MaxBasei32; + char HighBit; +}; + +const POW_INFO pow_info[] = +{ + { 0ULL, 0UL, 0, 0, 0 }, // 0 is a special case + { 0ULL, 0UL, 0, 0, 1 }, // 1 is a special case + { 3037000499ULL, 2147483647UL, 65535, 46340, 2 }, // 2 + { 2097152ULL, 1664510UL, 1625, 1290, 2 }, // 3 + { 55108ULL, 46340UL, 255, 215, 3 }, // 4 + { 6208ULL, 5404UL, 84, 73, 3 }, // 5 + { 1448ULL, 1290UL, 40, 35, 3 }, // 6 + { 511ULL, 463UL, 23, 21, 3 }, // 7 + { 234ULL, 215UL, 15, 14, 4 }, // 8 + { 128ULL, 118UL, 11, 10, 4 }, // 9 + { 78ULL, 73UL, 9, 8, 4 }, // 10 + { 52ULL, 49UL, 7, 7, 4 }, // 11 + { 38ULL, 35UL, 6, 5, 4 }, // 12 + { 28ULL, 27UL, 5, 5, 4 }, // 13 + { 22ULL, 21UL, 4, 4, 4 }, // 14 + { 18ULL, 17UL, 4, 4, 4 }, // 15 + { 15ULL, 14UL, 3, 3, 5 }, // 16 + { 13ULL, 12UL, 3, 3, 5 }, // 17 + { 11ULL, 10UL, 3, 3, 5 }, // 18 + { 9ULL, 9UL, 3, 3, 5 }, // 19 + { 8ULL, 8UL, 3, 2, 5 }, // 20 + { 8ULL, 7UL, 2, 2, 5 }, // 21 + { 7ULL, 7UL, 2, 2, 5 }, // 22 + { 6ULL, 6UL, 2, 2, 5 }, // 23 + { 6ULL, 5UL, 2, 2, 5 }, // 24 + { 5ULL, 5UL, 2, 2, 5 }, // 25 + { 5ULL, 5UL, 2, 2, 5 }, // 26 + { 5ULL, 4UL, 2, 2, 5 }, // 27 + { 4ULL, 4UL, 2, 2, 5 }, // 28 + { 4ULL, 4UL, 2, 2, 5 }, // 29 + { 4ULL, 4UL, 2, 2, 5 }, // 30 + { 4ULL, 4UL, 2, 1, 5 }, // 31 + { 3ULL, 3UL, 1, 1, 6 }, // 32 + { 3ULL, 3UL, 1, 1, 6 }, // 33 + { 3ULL, 3UL, 1, 1, 6 }, // 34 + { 3ULL, 3UL, 1, 1, 6 }, // 35 + { 3ULL, 3UL, 1, 1, 6 }, // 36 + { 3ULL, 3UL, 1, 1, 6 }, // 37 + { 3ULL, 3UL, 1, 1, 6 }, // 38 + { 3ULL, 3UL, 1, 1, 6 }, // 39 + { 2ULL, 2UL, 1, 1, 6 }, // 40 + { 2ULL, 2UL, 1, 1, 6 }, // 41 + { 2ULL, 2UL, 1, 1, 6 }, // 42 + { 2ULL, 2UL, 1, 1, 6 }, // 43 + { 2ULL, 2UL, 1, 1, 6 }, // 44 + { 2ULL, 2UL, 1, 1, 6 }, // 45 + { 2ULL, 2UL, 1, 1, 6 }, // 46 + { 2ULL, 2UL, 1, 1, 6 }, // 47 + { 2ULL, 2UL, 1, 1, 6 }, // 48 + { 2ULL, 2UL, 1, 1, 6 }, // 49 + { 2ULL, 2UL, 1, 1, 6 }, // 50 + { 2ULL, 2UL, 1, 1, 6 }, // 51 + { 2ULL, 2UL, 1, 1, 6 }, // 52 + { 2ULL, 2UL, 1, 1, 6 }, // 53 + { 2ULL, 2UL, 1, 1, 6 }, // 54 + { 2ULL, 2UL, 1, 1, 6 }, // 55 + { 2ULL, 2UL, 1, 1, 6 }, // 56 + { 2ULL, 2UL, 1, 1, 6 }, // 57 + { 2ULL, 2UL, 1, 1, 6 }, // 58 + { 2ULL, 2UL, 1, 1, 6 }, // 59 + { 2ULL, 2UL, 1, 1, 6 }, // 60 + { 2ULL, 2UL, 1, 1, 6 }, // 61 + { 2ULL, 2UL, 1, 1, 6 }, // 62 + { 2ULL, 1UL, 1, 1, 6 }, // 63 +}; + +int as_powi(int base, int exponent, bool& isOverflow) +{ + if( exponent < 0 ) + { + if( base == 0 ) + // Divide by zero + isOverflow = true; + else + // Result is less than 1, so it truncates to 0 + isOverflow = false; + + return 0; + } + else if( exponent == 0 && base == 0 ) + { + // Domain error + isOverflow = true; + return 0; + } + else if( exponent >= 31 ) + { + switch( base ) + { + case -1: + isOverflow = false; + return exponent & 1 ? -1 : 1; + case 0: + isOverflow = false; + break; + case 1: + isOverflow = false; + return 1; + default: + isOverflow = true; + break; + } + return 0; + } + else + { + const asWORD max_base = pow_info[exponent].MaxBasei32; + const char high_bit = pow_info[exponent].HighBit; + if( max_base != 0 && max_base < (base < 0 ? -base : base) ) + { + isOverflow = true; + return 0; // overflow + } + + int result = 1; + switch( high_bit ) + { + case 5: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 4: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 3: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 2: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 1: + if( exponent ) result *= base; + default: + isOverflow = false; + return result; + } + } +} + +asDWORD as_powu(asDWORD base, asDWORD exponent, bool& isOverflow) +{ + if( exponent == 0 && base == 0 ) + { + // Domain error + isOverflow = true; + return 0; + } + else if( exponent >= 32 ) + { + switch( base ) + { + case 0: + isOverflow = false; + break; + case 1: + isOverflow = false; + return 1; + default: + isOverflow = true; + break; + } + return 0; + } + else + { + const asWORD max_base = pow_info[exponent].MaxBaseu32; + const char high_bit = pow_info[exponent].HighBit; + if( max_base != 0 && max_base < base ) + { + isOverflow = true; + return 0; // overflow + } + + asDWORD result = 1; + switch( high_bit ) + { + case 5: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 4: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 3: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 2: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 1: + if( exponent ) result *= base; + default: + isOverflow = false; + return result; + } + } +} + +asINT64 as_powi64(asINT64 base, asINT64 exponent, bool& isOverflow) +{ + if( exponent < 0 ) + { + if( base == 0 ) + // Divide by zero + isOverflow = true; + else + // Result is less than 1, so it truncates to 0 + isOverflow = false; + + return 0; + } + else if( exponent == 0 && base == 0 ) + { + // Domain error + isOverflow = true; + return 0; + } + else if( exponent >= 63 ) + { + switch( base ) + { + case -1: + isOverflow = false; + return exponent & 1 ? -1 : 1; + case 0: + isOverflow = false; + break; + case 1: + isOverflow = false; + return 1; + default: + isOverflow = true; + break; + } + return 0; + } + else + { + const asDWORD max_base = pow_info[exponent].MaxBasei64; + const char high_bit = pow_info[exponent].HighBit; + if( max_base != 0 && max_base < (base < 0 ? -base : base) ) + { + isOverflow = true; + return 0; // overflow + } + + asINT64 result = 1; + switch( high_bit ) + { + case 6: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 5: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 4: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 3: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 2: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 1: + if( exponent ) result *= base; + default: + isOverflow = false; + return result; + } + } +} + +asQWORD as_powu64(asQWORD base, asQWORD exponent, bool& isOverflow) +{ + if( exponent == 0 && base == 0 ) + { + // Domain error + isOverflow = true; + return 0; + } + else if( exponent >= 64 ) + { + switch( base ) + { + case 0: + isOverflow = false; + break; + case 1: + isOverflow = false; + return 1; + default: + isOverflow = true; + break; + } + return 0; + } + else + { + const asQWORD max_base = pow_info[exponent].MaxBaseu64; + const char high_bit = pow_info[exponent].HighBit; + if( max_base != 0 && max_base < base ) + { + isOverflow = true; + return 0; // overflow + } + + asQWORD result = 1; + switch( high_bit ) + { + case 6: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 5: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 4: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 3: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 2: + if( exponent & 1 ) result *= base; + exponent >>= 1; + base *= base; + case 1: + if( exponent ) result *= base; + default: + isOverflow = false; + return result; + } + } +} + +END_AS_NAMESPACE + + + diff --git a/3rdparty/angelscript/src/as_datatype.cpp b/3rdparty/angelscript/src/as_datatype.cpp new file mode 100644 index 0000000..68486d1 --- /dev/null +++ b/3rdparty/angelscript/src/as_datatype.cpp @@ -0,0 +1,691 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_datatype.cpp +// +// This class describes the datatype for expressions during compilation +// + +#include "as_config.h" +#include "as_datatype.h" +#include "as_tokendef.h" +#include "as_typeinfo.h" +#include "as_objecttype.h" +#include "as_scriptengine.h" +#include "as_tokenizer.h" + +BEGIN_AS_NAMESPACE + +asCDataType::asCDataType() +{ + tokenType = ttUnrecognizedToken; + typeInfo = 0; + isReference = false; + isReadOnly = false; + isAuto = false; + isObjectHandle = false; + isConstHandle = false; + isHandleToAsHandleType = false; + ifHandleThenConst = false; +} + +asCDataType::asCDataType(const asCDataType &dt) +{ + tokenType = dt.tokenType; + typeInfo = dt.typeInfo; + isReference = dt.isReference; + isReadOnly = dt.isReadOnly; + isAuto = dt.isAuto; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; + isHandleToAsHandleType = dt.isHandleToAsHandleType; + ifHandleThenConst = dt.ifHandleThenConst; +} + +asCDataType::~asCDataType() +{ +} + +bool asCDataType::IsValid() const +{ + if( tokenType == ttUnrecognizedToken && + !isObjectHandle ) + return false; + + return true; +} + +asCDataType asCDataType::CreateType(asCTypeInfo *ti, bool isConst) +{ + asCDataType dt; + + dt.tokenType = ttIdentifier; + dt.typeInfo = ti; + dt.isReadOnly = isConst; + + return dt; +} + +asCDataType asCDataType::CreateAuto(bool isConst) +{ + asCDataType dt; + + dt.tokenType = ttIdentifier; + dt.isReadOnly = isConst; + dt.isAuto = true; + + return dt; +} + +asCDataType asCDataType::CreateObjectHandle(asCTypeInfo *ot, bool isConst) +{ + asCDataType dt; + + asASSERT(CastToObjectType(ot)); + + dt.tokenType = ttIdentifier; + dt.typeInfo = ot; + dt.isObjectHandle = true; + dt.isConstHandle = isConst; + + return dt; +} + +asCDataType asCDataType::CreatePrimitive(eTokenType tt, bool isConst) +{ + asCDataType dt; + + dt.tokenType = tt; + dt.isReadOnly = isConst; + + return dt; +} + +asCDataType asCDataType::CreateNullHandle() +{ + asCDataType dt; + + dt.tokenType = ttUnrecognizedToken; + dt.isReadOnly = true; + dt.isObjectHandle = true; + dt.isConstHandle = true; + + return dt; +} + +bool asCDataType::IsNullHandle() const +{ + if( tokenType == ttUnrecognizedToken && + typeInfo == 0 && + isObjectHandle ) + return true; + + return false; +} + +asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const +{ + if( IsNullHandle() ) + return ""; + + asCString str; + + if( isReadOnly ) + str = "const "; + + // If the type is not declared in the current namespace, then the namespace + // must always be informed to guarantee that the correct type is informed + if (includeNamespace || (typeInfo && typeInfo->nameSpace != currNs)) + { + if (typeInfo && typeInfo->nameSpace && typeInfo->nameSpace->name != "") + str += typeInfo->nameSpace->name + "::"; + } + if (typeInfo && typeInfo->nameSpace == 0) + { + // If funcDef->nameSpace is null it means the funcDef was declared as member of + // another type, in which case the scope should be built with the name of that type + str += CastToFuncdefType(typeInfo)->parentClass->name + "::"; + } + + if( tokenType != ttIdentifier ) + { + str += asCTokenizer::GetDefinition(tokenType); + } + else if( IsArrayType() && typeInfo && !typeInfo->engine->ep.expandDefaultArrayToTemplate ) + { + asCObjectType *ot = CastToObjectType(typeInfo); + asASSERT( ot && ot->templateSubTypes.GetLength() == 1 ); + str += ot->templateSubTypes[0].Format(currNs, includeNamespace); + str += "[]"; + } + else if(typeInfo) + { + str += typeInfo->name; + asCObjectType *ot = CastToObjectType(typeInfo); + if( ot && ot->templateSubTypes.GetLength() > 0 ) + { + str += "<"; + for( asUINT subtypeIndex = 0; subtypeIndex < ot->templateSubTypes.GetLength(); subtypeIndex++ ) + { + str += ot->templateSubTypes[subtypeIndex].Format(currNs, includeNamespace); + if( subtypeIndex != ot->templateSubTypes.GetLength()-1 ) + str += ","; + } + str += ">"; + } + } + else if( isAuto ) + { + str += ""; + } + else + { + str = ""; + } + + if( isObjectHandle ) + { + str += "@"; + if( isConstHandle ) + str += "const"; + } + + if( isReference ) + str += "&"; + + return str; +} + +asCDataType &asCDataType::operator =(const asCDataType &dt) +{ + tokenType = dt.tokenType; + isReference = dt.isReference; + typeInfo = dt.typeInfo; + isReadOnly = dt.isReadOnly; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; + isAuto = dt.isAuto; + isHandleToAsHandleType = dt.isHandleToAsHandleType; + ifHandleThenConst = dt.ifHandleThenConst; + + return (asCDataType &)*this; +} + +int asCDataType::MakeHandle(bool b, bool acceptHandleForScope) +{ + if( !b ) + { + isObjectHandle = false; + isConstHandle = false; + isHandleToAsHandleType = false; + } + else + { + if( isAuto ) + { + isObjectHandle = true; + } + else if( !isObjectHandle ) + { + // Only reference types are allowed to be handles, + // but not nohandle reference types, and not scoped references + // (except when returned from registered function) + // funcdefs are special reference types and support handles + // value types with asOBJ_ASHANDLE are treated as a handle + if( (!typeInfo || + !((typeInfo->flags & asOBJ_REF) || (typeInfo->flags & asOBJ_TEMPLATE_SUBTYPE) || (typeInfo->flags & asOBJ_ASHANDLE) || (typeInfo->flags & asOBJ_FUNCDEF)) || + (typeInfo->flags & asOBJ_NOHANDLE) || + ((typeInfo->flags & asOBJ_SCOPED) && !acceptHandleForScope)) ) + return -1; + + isObjectHandle = b; + isConstHandle = false; + + // ASHANDLE supports being handle, but as it really is a value type it will not be marked as a handle + if( (typeInfo->flags & asOBJ_ASHANDLE) ) + { + isObjectHandle = false; + isHandleToAsHandleType = true; + } + } + } + + return 0; +} + +int asCDataType::MakeArray(asCScriptEngine *engine, asCModule *module) +{ + if( engine->defaultArrayObjectType == 0 ) + return asINVALID_TYPE; + + bool tmpIsReadOnly = isReadOnly; + isReadOnly = false; + asCArray subTypes; + subTypes.PushLast(*this); + asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, subTypes, module); + isReadOnly = tmpIsReadOnly; + + isObjectHandle = false; + isConstHandle = false; + + typeInfo = at; + tokenType = ttIdentifier; + + return 0; +} + +int asCDataType::MakeReference(bool b) +{ + isReference = b; + + return 0; +} + +int asCDataType::MakeReadOnly(bool b) +{ + if( isObjectHandle ) + { + isConstHandle = b; + return 0; + } + + isReadOnly = b; + return 0; +} + +int asCDataType::MakeHandleToConst(bool b) +{ + if( !isObjectHandle ) return -1; + + isReadOnly = b; + return 0; +} + +bool asCDataType::SupportHandles() const +{ + if( typeInfo && + (typeInfo->flags & (asOBJ_REF | asOBJ_ASHANDLE | asOBJ_FUNCDEF)) && + !(typeInfo->flags & asOBJ_NOHANDLE) && + !isObjectHandle ) + return true; + + return false; +} + +bool asCDataType::CanBeInstantiated() const +{ + if( GetSizeOnStackDWords() == 0 ) // Void + return false; + + if( !IsObject() && !IsFuncdef() ) // Primitives + return true; + + if (IsNullHandle()) // null + return false; + + if( IsObjectHandle() && !(typeInfo->flags & asOBJ_NOHANDLE) ) // Handles + return true; + + // Funcdefs cannot be instantiated without being handles + // The exception being delegates, but these can only be created as temporary objects + if (IsFuncdef()) + return false; + + asCObjectType *ot = CastToObjectType(typeInfo); + if( ot && (ot->flags & asOBJ_REF) && ot->beh.factories.GetLength() == 0 ) // ref types without factories + return false; + + if( ot && (ot->flags & asOBJ_ABSTRACT) && !IsObjectHandle() ) // Can't instantiate abstract classes + return false; + + return true; +} + +bool asCDataType::IsAbstractClass() const +{ + return typeInfo && (typeInfo->flags & asOBJ_ABSTRACT) ? true : false; +} + +bool asCDataType::IsInterface() const +{ + if (typeInfo == 0) + return false; + + asCObjectType *ot = CastToObjectType(typeInfo); + return ot && ot->IsInterface(); +} + +bool asCDataType::CanBeCopied() const +{ + // All primitives can be copied + if( IsPrimitive() ) return true; + + // Plain-old-data structures can always be copied + if( typeInfo->flags & asOBJ_POD ) return true; + + // It must be possible to instantiate the type + if( !CanBeInstantiated() ) return false; + + // It must have a default constructor or factory + asCObjectType *ot = CastToObjectType(typeInfo); + if( ot && ot->beh.construct == 0 && + ot->beh.factory == 0 ) return false; + + // It must be possible to copy the type + if( ot && ot->beh.copy == 0 ) return false; + + return true; +} + +bool asCDataType::IsReadOnly() const +{ + if( isObjectHandle ) + return isConstHandle; + + return isReadOnly; +} + +bool asCDataType::IsHandleToConst() const +{ + if( !isObjectHandle ) return false; + return isReadOnly; +} + +bool asCDataType::IsObjectConst() const +{ + if( IsObjectHandle() ) + return IsHandleToConst(); + + return IsReadOnly(); +} + +// TODO: 3.0.0: This should be removed +bool asCDataType::IsArrayType() const +{ + // This is only true if the type used is the default array type, i.e. the one used for the [] syntax form + if( typeInfo && typeInfo->engine->defaultArrayObjectType ) + return typeInfo->name == typeInfo->engine->defaultArrayObjectType->name; + + return false; +} + +bool asCDataType::IsTemplate() const +{ + if( typeInfo && (typeInfo->flags & asOBJ_TEMPLATE) ) + return true; + + return false; +} + +bool asCDataType::IsScriptObject() const +{ + if( typeInfo && (typeInfo->flags & asOBJ_SCRIPT_OBJECT) ) + return true; + + return false; +} + +asCDataType asCDataType::GetSubType(asUINT subtypeIndex) const +{ + asASSERT(typeInfo); + asCObjectType *ot = CastToObjectType(typeInfo); + return ot->templateSubTypes[subtypeIndex]; +} + + +bool asCDataType::operator !=(const asCDataType &dt) const +{ + return !(*this == dt); +} + +bool asCDataType::operator ==(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReference != dt.isReference ) return false; + if( isReadOnly != dt.isReadOnly ) return false; + if( isConstHandle != dt.isConstHandle ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptRef(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReadOnly != dt.isReadOnly ) return false; + if( isConstHandle != dt.isConstHandle ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptRefAndConst(const asCDataType &dt) const +{ + // Check base type + if( tokenType != dt.tokenType ) return false; + if( typeInfo != dt.typeInfo ) return false; + if( isObjectHandle != dt.isObjectHandle ) return false; + if( isObjectHandle ) + if( isReadOnly != dt.isReadOnly ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptConst(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReference != dt.isReference ) return false; + + return true; +} + +bool asCDataType::IsPrimitive() const +{ + // Enumerations are primitives + if( IsEnumType() ) + return true; + + // A registered object is never a primitive neither is a pointer nor an array + if( typeInfo ) + return false; + + // Null handle doesn't have a typeInfo, but it is not a primitive + if( tokenType == ttUnrecognizedToken ) + return false; + + return true; +} + +bool asCDataType::IsMathType() const +{ + if( tokenType == ttInt || tokenType == ttInt8 || tokenType == ttInt16 || tokenType == ttInt64 || + tokenType == ttUInt || tokenType == ttUInt8 || tokenType == ttUInt16 || tokenType == ttUInt64 || + tokenType == ttFloat || tokenType == ttDouble ) + return true; + + return false; +} + +bool asCDataType::IsIntegerType() const +{ + if( tokenType == ttInt || + tokenType == ttInt8 || + tokenType == ttInt16 || + tokenType == ttInt64 ) + return true; + + // Enums are also integer types + return IsEnumType(); +} + +bool asCDataType::IsUnsignedType() const +{ + if( tokenType == ttUInt || + tokenType == ttUInt8 || + tokenType == ttUInt16 || + tokenType == ttUInt64 ) + return true; + + return false; +} + +bool asCDataType::IsFloatType() const +{ + if( tokenType == ttFloat ) + return true; + + return false; +} + +bool asCDataType::IsDoubleType() const +{ + if( tokenType == ttDouble ) + return true; + + return false; +} + +bool asCDataType::IsBooleanType() const +{ + if( tokenType == ttBool ) + return true; + + return false; +} + +bool asCDataType::IsObject() const +{ + if( IsPrimitive() ) + return false; + + // Null handle doesn't have an object type but should still be considered an object + if( typeInfo == 0 ) + return IsNullHandle(); + + // Template subtypes shouldn't be considered objects + return CastToObjectType(typeInfo) ? true : false; +} + +bool asCDataType::IsFuncdef() const +{ + if (typeInfo && (typeInfo->flags & asOBJ_FUNCDEF)) + return true; + + return false; +} + +int asCDataType::GetSizeInMemoryBytes() const +{ + if( typeInfo != 0 ) + return typeInfo->size; + + if( tokenType == ttVoid ) + return 0; + + if( tokenType == ttInt8 || + tokenType == ttUInt8 ) + return 1; + + if( tokenType == ttInt16 || + tokenType == ttUInt16 ) + return 2; + + if( tokenType == ttDouble || + tokenType == ttInt64 || + tokenType == ttUInt64 ) + return 8; + + if( tokenType == ttBool ) + return AS_SIZEOF_BOOL; + + // null handle + if( tokenType == ttUnrecognizedToken ) + return 4*AS_PTR_SIZE; + + return 4; +} + +int asCDataType::GetSizeInMemoryDWords() const +{ + int s = GetSizeInMemoryBytes(); + if( s == 0 ) return 0; + if( s <= 4 ) return 1; + + // Pad the size to 4 bytes + if( s & 0x3 ) + s += 4 - (s & 0x3); + + return s/4; +} + +int asCDataType::GetSizeOnStackDWords() const +{ + // If the type is the variable type then the typeid is stored on the stack too + int size = tokenType == ttQuestion ? 1 : 0; + + if( isReference ) return AS_PTR_SIZE + size; + if( typeInfo && !IsEnumType() ) return AS_PTR_SIZE + size; + + return GetSizeInMemoryDWords() + size; +} + +#ifdef WIP_16BYTE_ALIGN +int asCDataType::GetAlignment() const +{ + if( typeInfo == NULL ) + { + // TODO: Small primitives should not be aligned to 4 byte boundaries + return 4; //Default alignment + } + + return typeInfo->alignment; +} +#endif + +asSTypeBehaviour *asCDataType::GetBehaviour() const +{ + if (!typeInfo) return 0; + asCObjectType *ot = CastToObjectType(typeInfo); + return ot ? &ot->beh : 0; +} + +bool asCDataType::IsEnumType() const +{ + // Do a sanity check on the objectType, to verify that we aren't trying to access memory after it has been released + asASSERT(typeInfo == 0 || typeInfo->name.GetLength() < 100); + + if (typeInfo && (typeInfo->flags & asOBJ_ENUM)) + return true; + + return false; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_gc.cpp b/3rdparty/angelscript/src/as_gc.cpp new file mode 100644 index 0000000..38a527f --- /dev/null +++ b/3rdparty/angelscript/src/as_gc.cpp @@ -0,0 +1,975 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_gc.cpp +// +// The implementation of the garbage collector +// + + +#include + +#include "as_gc.h" +#include "as_scriptengine.h" +#include "as_scriptobject.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +asCGarbageCollector::asCGarbageCollector() +{ + engine = 0; + detectState = clearCounters_init; + destroyNewState = destroyGarbage_init; + destroyOldState = destroyGarbage_init; + numDestroyed = 0; + numNewDestroyed = 0; + numDetected = 0; + numAdded = 0; + isProcessing = false; + + seqAtSweepStart[0] = 0; + seqAtSweepStart[1] = 0; + seqAtSweepStart[2] = 0; +} + +asCGarbageCollector::~asCGarbageCollector() +{ + // This local typedef is done to workaround a compiler error on + // MSVC6 when using the typedef declared in the class definition + typedef asSMapNode_t node_t; + for( asUINT n = 0; n < freeNodes.GetLength(); n++ ) + asDELETE(freeNodes[n], node_t); + freeNodes.SetLength(0); +} + +int asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) +{ + if( obj == 0 || objType == 0 ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_GC_RECEIVED_NULL_PTR); + return asINVALID_ARG; + } + + engine->CallObjectMethod(obj, objType->beh.addref); + asSObjTypePair ot = {obj, objType, 0}; + + // Invoke the garbage collector to destroy a little garbage as new comes in + // This will maintain the number of objects in the GC at a maintainable level without + // halting the application, and without burdening the application with manually invoking the + // garbage collector. + if( engine->ep.autoGarbageCollect && gcNewObjects.GetLength() ) + { + // If the GC is already processing in another thread, then don't try this again + if( TRYENTERCRITICALSECTION(gcCollecting) ) + { + // Skip this if the GC is already running in this thread + if( !isProcessing ) + { + isProcessing = true; + + // TODO: The number of iterations should be dynamic, and increase + // if the number of objects in the garbage collector grows high + + // Run one step of DetectGarbage + if( gcOldObjects.GetLength() ) + { + IdentifyGarbageWithCyclicRefs(); + DestroyOldGarbage(); + } + + // Run a few steps of DestroyGarbage + int iter = (int)gcNewObjects.GetLength(); + if( iter > 10 ) iter = 10; + while( iter-- > 0 ) + DestroyNewGarbage(); + + isProcessing = false; + } + + LEAVECRITICALSECTION(gcCollecting); + } + } + + // Add the data to the gcObjects array in a critical section as + // another thread might be calling this method at the same time + ENTERCRITICALSECTION(gcCritical); + ot.seqNbr = numAdded++; + gcNewObjects.PushLast(ot); + LEAVECRITICALSECTION(gcCritical); + + return ot.seqNbr; +} + +int asCGarbageCollector::GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asITypeInfo **type) +{ + if( seqNbr ) *seqNbr = 0; + if( obj ) *obj = 0; + if( type ) *type = 0; + + ENTERCRITICALSECTION(gcCritical); + asSObjTypePair *o = 0; + asUINT newObjs = asUINT(gcNewObjects.GetLength()); + if( idx < newObjs ) + o = &gcNewObjects[idx]; + else if( idx < gcOldObjects.GetLength() + newObjs ) + o = &gcOldObjects[idx-newObjs]; + else + { + LEAVECRITICALSECTION(gcCritical); + return asINVALID_ARG; + } + if( seqNbr ) *seqNbr = o->seqNbr; + if( obj ) *obj = o->obj; + if( type ) *type = o->type; + LEAVECRITICALSECTION(gcCritical); + + return asSUCCESS; +} + +// TODO: Should have a flag to tell the garbage collector to automatically determine how many iterations are needed +// It should then gather statistics such as how many objects has been created since last run, and how many objects +// are destroyed per iteration, and how many objects are detected as cyclic garbage per iteration. +// It should try to reach a stable number of objects, i.e. so that on average the number of objects added to +// the garbage collector is the same as the number of objects destroyed. And it should try to minimize the number +// of iterations of detections that must be executed per cycle while still identifying the cyclic garbage +// These variables should also be available for inspection through the gcstatistics. +int asCGarbageCollector::GarbageCollect(asDWORD flags, asUINT iterations) +{ + // If the GC is already processing in another thread, then don't enter here again + if( TRYENTERCRITICALSECTION(gcCollecting) ) + { + // If the GC is already processing in this thread, then don't enter here again + if( isProcessing ) + { + LEAVECRITICALSECTION(gcCollecting); + return 1; + } + + isProcessing = true; + + bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); + bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); + + if( flags & asGC_FULL_CYCLE ) + { + // Reset the state + if( doDetect ) + { + // Move all new objects to the old list, so we guarantee that all is detected + MoveAllObjectsToOldList(); + detectState = clearCounters_init; + } + if( doDestroy ) + { + destroyNewState = destroyGarbage_init; + destroyOldState = destroyGarbage_init; + } + + // The full cycle only works with the objects in the old list so that the + // set of objects scanned for garbage is fixed even if new objects are added + // by other threads in parallel. + unsigned int count = (unsigned int)(gcOldObjects.GetLength()); + for(;;) + { + // Detect all garbage with cyclic references + if( doDetect ) + while( IdentifyGarbageWithCyclicRefs() == 1 ) {} + + // Now destroy all known garbage + if( doDestroy ) + { + if( !doDetect ) + while( DestroyNewGarbage() == 1 ) {} + while( DestroyOldGarbage() == 1 ) {} + } + + // Run another iteration if any garbage was destroyed + if( count != (unsigned int)(gcOldObjects.GetLength()) ) + count = (unsigned int)(gcOldObjects.GetLength()); + else + break; + } + + isProcessing = false; + LEAVECRITICALSECTION(gcCollecting); + return 0; + } + else + { + while( iterations-- > 0 ) + { + // Destroy the garbage that we know of + if( doDestroy ) + { + DestroyNewGarbage(); + DestroyOldGarbage(); + } + + // Run another incremental step of the identification of cyclic references + if( doDetect && gcOldObjects.GetLength() > 0 ) + IdentifyGarbageWithCyclicRefs(); + } + } + + isProcessing = false; + LEAVECRITICALSECTION(gcCollecting); + } + + // Return 1 to indicate that the cycle wasn't finished + return 1; +} + +// TODO: Additional statistics to gather +// +// - How many objects are added on average between each destroyed object +// - How many objects are added on average between each detected object +// - how many iterations are needed for each destroyed object +// - how many iterations are needed for each detected object +// +// The average must have a decay so that long running applications will not suffer +// from objects being created early on in the application and then never destroyed. +// +// This ought to be possible to accomplish by holding two buckets. +// Numbers will be accumulated in one bucket while the other is held fixed. +// When returning the average it should use a weighted average between the two buckets using the size as weight. +// When a bucket is filled up, the buckets are switched, and then new bucket is emptied to gather new statistics. +void asCGarbageCollector::GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const +{ + // It is not necessary to protect this with critical sections, however + // as it is not protected the variables can be filled in slightly different + // moments and might not match perfectly when inspected by the application + // afterwards. + + if( currentSize ) + *currentSize = (asUINT)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); + + if( totalDestroyed ) + *totalDestroyed = numDestroyed; + + if( totalDetected ) + *totalDetected = numDetected; + + if( newObjects ) + *newObjects = (asUINT)gcNewObjects.GetLength(); + + if( totalNewDestroyed ) + *totalNewDestroyed = numNewDestroyed; +} + +asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetNewObjectAtIdx(int idx) +{ + // We need to protect this access with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + asSObjTypePair gcObj = gcNewObjects[idx]; + LEAVECRITICALSECTION(gcCritical); + + return gcObj; +} + +asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetOldObjectAtIdx(int idx) +{ + // We need to protect this access with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + asSObjTypePair gcObj = gcOldObjects[idx]; + LEAVECRITICALSECTION(gcCritical); + + return gcObj; +} + +void asCGarbageCollector::RemoveNewObjectAtIdx(int idx) +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + if( idx == (int)gcNewObjects.GetLength() - 1) + gcNewObjects.PopLast(); + else + gcNewObjects[idx] = gcNewObjects.PopLast(); + LEAVECRITICALSECTION(gcCritical); +} + +void asCGarbageCollector::RemoveOldObjectAtIdx(int idx) +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + if( idx == (int)gcOldObjects.GetLength() - 1) + gcOldObjects.PopLast(); + else + gcOldObjects[idx] = gcOldObjects.PopLast(); + LEAVECRITICALSECTION(gcCritical); +} + +void asCGarbageCollector::MoveObjectToOldList(int idx) +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + gcOldObjects.PushLast(gcNewObjects[idx]); + if( idx == (int)gcNewObjects.GetLength() - 1) + gcNewObjects.PopLast(); + else + gcNewObjects[idx] = gcNewObjects.PopLast(); + LEAVECRITICALSECTION(gcCritical); +} + +void asCGarbageCollector::MoveAllObjectsToOldList() +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + if( gcOldObjects.Concatenate(gcNewObjects) ) + gcNewObjects.SetLength(0); + LEAVECRITICALSECTION(gcCritical); +} + +int asCGarbageCollector::DestroyNewGarbage() +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + for(;;) + { + switch( destroyNewState ) + { + case destroyGarbage_init: + { + // If there are no objects to be freed then don't start + if( gcNewObjects.GetLength() == 0 ) + return 0; + + // Update the seqAtSweepStart which is used to determine when + // to move an object from the new set to the old set + seqAtSweepStart[0] = seqAtSweepStart[1]; + seqAtSweepStart[1] = seqAtSweepStart[2]; + seqAtSweepStart[2] = numAdded; + + destroyNewIdx = (asUINT)-1; + destroyNewState = destroyGarbage_loop; + } + break; + + case destroyGarbage_loop: + case destroyGarbage_haveMore: + { + // If the refCount has reached 1, then only the GC still holds a + // reference to the object, thus we don't need to worry about the + // application touching the objects during collection. + + // Destroy all objects that have refCount == 1. If any objects are + // destroyed, go over the list again, because it may have made more + // objects reach refCount == 1. + if( ++destroyNewIdx < gcNewObjects.GetLength() ) + { + asSObjTypePair gcObj = GetNewObjectAtIdx(destroyNewIdx); + if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 ) + { + // Release the object immediately + + // Make sure the refCount is really 0, because the + // destructor may have increased the refCount again. + bool addRef = false; + if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT ) + { + // Script objects may actually be resurrected in the destructor + int refCount = ((asCScriptObject*)gcObj.obj)->Release(); + if( refCount > 0 ) addRef = true; + } + else + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); + + // Was the object really destroyed? + if( !addRef ) + { + numDestroyed++; + numNewDestroyed++; + RemoveNewObjectAtIdx(destroyNewIdx); + destroyNewIdx--; + } + else + { + // Since the object was resurrected in the + // destructor, we must add our reference again + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); + } + + destroyNewState = destroyGarbage_haveMore; + } + // Check if this object has been inspected 3 times already, and if so move it to the + // set of old objects that are less likely to become garbage in a short time + // TODO: Is 3 really a good value? Should the number of times be dynamic? + else if( gcObj.seqNbr < seqAtSweepStart[0] ) + { + // We've already verified this object multiple times. It is likely + // to live for quite a long time so we'll move it to the list if old objects + MoveObjectToOldList(destroyNewIdx); + destroyNewIdx--; + } + + // Allow the application to work a little + return 1; + } + else + { + if( destroyNewState == destroyGarbage_haveMore ) + { + // Restart the cycle + destroyNewState = destroyGarbage_init; + } + else + { + // Restart the cycle + destroyNewState = destroyGarbage_init; + + // Return 0 to tell the application that there + // is no more garbage to destroy at the moment + return 0; + } + } + } + break; + } + } + + // Shouldn't reach this point + UNREACHABLE_RETURN; +} + +int asCGarbageCollector::ReportAndReleaseUndestroyedObjects() +{ + // This function will only be called as the engine is shutting down + + int items = 0; + for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ ) + { + asSObjTypePair gcObj = GetOldObjectAtIdx(n); + + int refCount = 0; + if( gcObj.type->beh.gcGetRefCount && engine->scriptFunctions[gcObj.type->beh.gcGetRefCount] ) + refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount); + + // Report the object as not being properly destroyed + asCString msg; + msg.Format(TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d, gcObj.seqNbr, gcObj.type->name.AddressOf(), refCount - 1); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); + + // Add additional info for builtin types + if( gcObj.type->name == "$func" ) + { + // Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading + // We need to show the function type too as for example delegates do not have a name + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast(gcObj.obj)->GetName(), reinterpret_cast(gcObj.obj)->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + else if( gcObj.type->name == "$obj" ) + { + msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast(gcObj.obj)->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + + // Release the reference that the GC holds if the release functions is still available + if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] ) + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); + + items++; + } + return items; +} + +int asCGarbageCollector::DestroyOldGarbage() +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + for(;;) + { + switch( destroyOldState ) + { + case destroyGarbage_init: + { + // If there are no objects to be freed then don't start + if( gcOldObjects.GetLength() == 0 ) + return 0; + + destroyOldIdx = (asUINT)-1; + destroyOldState = destroyGarbage_loop; + } + break; + + case destroyGarbage_loop: + case destroyGarbage_haveMore: + { + // If the refCount has reached 1, then only the GC still holds a + // reference to the object, thus we don't need to worry about the + // application touching the objects during collection. + + // Destroy all objects that have refCount == 1. If any objects are + // destroyed, go over the list again, because it may have made more + // objects reach refCount == 1. + if( ++destroyOldIdx < gcOldObjects.GetLength() ) + { + asSObjTypePair gcObj = GetOldObjectAtIdx(destroyOldIdx); + + if( gcObj.type->beh.gcGetRefCount == 0 ) + { + // If circular references are formed with registered types that hasn't + // registered the GC behaviours, then the engine may be forced to free + // the object type before the actual object instance. In this case we + // will be forced to skip the destruction of the objects, so as not to + // crash the application. + asCString msg; + msg.Format(TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s, gcObj.seqNbr, gcObj.type->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); + + // Just remove the object, as we will not bother to destroy it + numDestroyed++; + RemoveOldObjectAtIdx(destroyOldIdx); + destroyOldIdx--; + } + else if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 ) + { + // Release the object immediately + + // Make sure the refCount is really 0, because the + // destructor may have increased the refCount again. + bool addRef = false; + if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT ) + { + // Script objects may actually be resurrected in the destructor + int refCount = ((asCScriptObject*)gcObj.obj)->Release(); + if( refCount > 0 ) addRef = true; + } + else + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); + + // Was the object really destroyed? + if( !addRef ) + { + numDestroyed++; + RemoveOldObjectAtIdx(destroyOldIdx); + destroyOldIdx--; + } + else + { + // Since the object was resurrected in the + // destructor, we must add our reference again + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); + } + + destroyOldState = destroyGarbage_haveMore; + } + + // Allow the application to work a little + return 1; + } + else + { + if( destroyOldState == destroyGarbage_haveMore ) + { + // Restart the cycle + destroyOldState = destroyGarbage_init; + } + else + { + // Restart the cycle + destroyOldState = destroyGarbage_init; + + // Return 0 to tell the application that there + // is no more garbage to destroy at the moment + return 0; + } + } + } + break; + } + } + + // Shouldn't reach this point + UNREACHABLE_RETURN; +} + +int asCGarbageCollector::IdentifyGarbageWithCyclicRefs() +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + for(;;) + { + switch( detectState ) + { + case clearCounters_init: + detectState = clearCounters_loop; + break; + + case clearCounters_loop: + { + // Decrease reference counter for all objects removed from the map + asSMapNode *cursor = 0; + gcMap.MoveFirst(&cursor); + if( cursor ) + { + void *obj = gcMap.GetKey(cursor); + asSIntTypePair it = gcMap.GetValue(cursor); + + engine->CallObjectMethod(obj, it.type->beh.release); + + ReturnNode(gcMap.Remove(cursor)); + + return 1; + } + + detectState = buildMap_init; + } + break; + + case buildMap_init: + detectIdx = 0; + detectState = buildMap_loop; + break; + + case buildMap_loop: + { + // Build a map of objects that will be checked, the map will + // hold the object pointer as key, and the gcCount and the + // object's type as value. As objects are added to the map the + // gcFlag must be set in the objects, so we can be verify if + // the object is accessed during the GC cycle. + + // If an object is removed from the gcObjects list during the + // iteration of this step, it is possible that an object won't + // be used during the analyzing for cyclic references. This + // isn't a problem, as the next time the GC cycle starts the + // object will be verified. + if( detectIdx < gcOldObjects.GetLength() ) + { + // Add the gc count for this object + asSObjTypePair gcObj = GetOldObjectAtIdx(detectIdx); + + int refCount = 0; + if( gcObj.type->beh.gcGetRefCount ) + refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount); + + if( refCount > 1 ) + { + asSIntTypePair it = {refCount-1, gcObj.type}; + + gcMap.Insert(GetNode(gcObj.obj, it)); + + // Increment the object's reference counter when putting it in the map + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); + + // Mark the object so that we can + // see if it has changed since read + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.gcSetFlag); + } + + detectIdx++; + + // Let the application work a little + return 1; + } + else + detectState = countReferences_init; + } + break; + + case countReferences_init: + { + gcMap.MoveFirst(&gcMapCursor); + detectState = countReferences_loop; + } + break; + + case countReferences_loop: + { + // Call EnumReferences on all objects in the map to count the number + // of references reachable from between objects in the map. If all + // references for an object in the map is reachable from other objects + // in the map, then we know that no outside references are held for + // this object, thus it is a potential dead object in a circular reference. + + // If the gcFlag is cleared for an object we consider the object alive + // and referenced from outside the GC, thus we don't enumerate its references. + + // Any new objects created after this step in the GC cycle won't be + // in the map, and is thus automatically considered alive. + if( gcMapCursor ) + { + void *obj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + if( engine->CallObjectMethodRetBool(obj, type->beh.gcGetFlag) ) + { + engine->CallObjectMethod(obj, engine, type->beh.gcEnumReferences); + } + + // Allow the application to work a little + return 1; + } + else + detectState = detectGarbage_init; + } + break; + + case detectGarbage_init: + { + gcMap.MoveFirst(&gcMapCursor); + liveObjects.SetLength(0); + detectState = detectGarbage_loop1; + } + break; + + case detectGarbage_loop1: + { + // All objects that are known not to be dead must be removed from the map, + // along with all objects they reference. What remains in the map after + // this pass is sure to be dead objects in circular references. + + // An object is considered alive if its gcFlag is cleared, or all the + // references were not found in the map. + + // Add all alive objects from the map to the liveObjects array + if( gcMapCursor ) + { + asSMapNode *cursor = gcMapCursor; + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + void *obj = gcMap.GetKey(cursor); + asSIntTypePair it = gcMap.GetValue(cursor); + + bool gcFlag = engine->CallObjectMethodRetBool(obj, it.type->beh.gcGetFlag); + if( !gcFlag || it.i > 0 ) + { + liveObjects.PushLast(obj); + } + + // Allow the application to work a little + return 1; + } + else + detectState = detectGarbage_loop2; + } + break; + + case detectGarbage_loop2: + { + // In this step we are actually removing the alive objects from the map. + // As the object is removed, all the objects it references are added to the + // liveObjects list, by calling EnumReferences. Only objects still in the map + // will be added to the liveObjects list. + if( liveObjects.GetLength() ) + { + void *gcObj = liveObjects.PopLast(); + asCObjectType *type = 0; + + // Remove the object from the map to mark it as alive + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, gcObj) ) + { + type = gcMap.GetValue(cursor).type; + ReturnNode(gcMap.Remove(cursor)); + + // We need to decrease the reference count again as we remove the object from the map + engine->CallObjectMethod(gcObj, type->beh.release); + + // Enumerate all the object's references so that they too can be marked as alive + engine->CallObjectMethod(gcObj, engine, type->beh.gcEnumReferences); + } + + // Allow the application to work a little + return 1; + } + else + detectState = verifyUnmarked_init; + } + break; + + case verifyUnmarked_init: + gcMap.MoveFirst(&gcMapCursor); + detectState = verifyUnmarked_loop; + break; + + case verifyUnmarked_loop: + { + // In this step we must make sure that none of the objects still in the map + // has been touched by the application. If they have then we must run the + // detectGarbage loop once more. + if( gcMapCursor ) + { + void *gcObj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + + bool gcFlag = engine->CallObjectMethodRetBool(gcObj, type->beh.gcGetFlag); + if( !gcFlag ) + { + // The unmarked object was touched, rerun the detectGarbage loop + detectState = detectGarbage_init; + } + else + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + // Allow the application to work a little + return 1; + } + else + { + // No unmarked object was touched, we can now be sure + // that objects that have gcCount == 0 really is garbage + detectState = breakCircles_init; + } + } + break; + + case breakCircles_init: + { + gcMap.MoveFirst(&gcMapCursor); + detectState = breakCircles_loop; + } + break; + + case breakCircles_loop: + case breakCircles_haveGarbage: + { + // All objects in the map are now known to be dead objects + // kept alive through circular references. To be able to free + // these objects we need to force the breaking of the circle + // by having the objects release their references. + if( gcMapCursor ) + { + numDetected++; + void *gcObj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + if( type->flags & asOBJ_SCRIPT_OBJECT ) + { + // For script objects we must call the class destructor before + // releasing the references, otherwise the destructor may not + // be able to perform the necessary clean-up as the handles will + // be null. + reinterpret_cast(gcObj)->CallDestructor(); + } + engine->CallObjectMethod(gcObj, engine, type->beh.gcReleaseAllReferences); + + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + detectState = breakCircles_haveGarbage; + + // Allow the application to work a little + return 1; + } + else + { + // If no garbage was detected we can finish now + if( detectState != breakCircles_haveGarbage ) + { + // Restart the GC + detectState = clearCounters_init; + return 0; + } + else + { + // Restart the GC + detectState = clearCounters_init; + return 1; + } + } + } + } // switch + } + + // Shouldn't reach this point + UNREACHABLE_RETURN; +} + +asCGarbageCollector::asSMapNode_t *asCGarbageCollector::GetNode(void *obj, asSIntTypePair it) +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + asSMapNode_t *node; + if( freeNodes.GetLength() ) + node = freeNodes.PopLast(); + else + { + node = asNEW(asSMapNode_t); + if( !node ) + { + // Out of memory + return 0; + } + } + + node->Init(obj, it); + return node; +} + +void asCGarbageCollector::ReturnNode(asSMapNode_t *node) +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + if( node ) + freeNodes.PushLast(node); +} + +void asCGarbageCollector::GCEnumCallback(void *reference) +{ + // This function will only be called within the critical section gcCollecting + asASSERT(isProcessing); + + if( detectState == countReferences_loop ) + { + // Find the reference in the map + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, reference) ) + { + // Decrease the counter in the map for the reference + gcMap.GetValue(cursor).i--; + } + } + else if( detectState == detectGarbage_loop2 ) + { + // Find the reference in the map + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, reference) ) + { + // Add the object to the list of objects to mark as alive + liveObjects.PushLast(reference); + } + } +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_generic.cpp b/3rdparty/angelscript/src/as_generic.cpp new file mode 100644 index 0000000..14dd9cb --- /dev/null +++ b/3rdparty/angelscript/src/as_generic.cpp @@ -0,0 +1,534 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_generic.cpp +// +// This class handles the call to a function registered with asCALL_GENERIC +// + +#include "as_generic.h" +#include "as_scriptfunction.h" +#include "as_objecttype.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +// TODO: runtime optimize: The access to the arguments should be optimized so that code +// doesn't have to count the position of the argument with every call + +// internal +asCGeneric::asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer) +{ + this->engine = engine; + this->sysFunction = sysFunction; + this->currentObject = currentObject; + this->stackPointer = stackPointer; + + objectRegister = 0; + returnVal = 0; +} + +// internal +asCGeneric::~asCGeneric() +{ +} + +// interface +void *asCGeneric::GetAuxiliary() const +{ + return sysFunction->GetAuxiliary(); +} + +// interface +asIScriptEngine *asCGeneric::GetEngine() const +{ + return (asIScriptEngine*)engine; +} + +// interface +asIScriptFunction *asCGeneric::GetFunction() const +{ + return sysFunction; +} + +// interface +void *asCGeneric::GetObject() +{ + return currentObject; +} + +// interface +int asCGeneric::GetObjectTypeId() const +{ + asCDataType dt = asCDataType::CreateType(sysFunction->objectType, false); + return engine->GetTypeIdFromDataType(dt); +} + +// interface +int asCGeneric::GetArgCount() const +{ + return (int)sysFunction->parameterTypes.GetLength(); +} + +// interface +asBYTE asCGeneric::GetArgByte(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 1 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asBYTE*)&stackPointer[offset]; +} + +// interface +asWORD asCGeneric::GetArgWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 2 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asWORD*)&stackPointer[offset]; +} + +// interface +asDWORD asCGeneric::GetArgDWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 4 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asDWORD*)&stackPointer[offset]; +} + +// interface +asQWORD asCGeneric::GetArgQWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 8 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asQWORD*)(&stackPointer[offset]); +} + +// interface +float asCGeneric::GetArgFloat(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 4 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(float*)(&stackPointer[offset]); +} + +// interface +double asCGeneric::GetArgDouble(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 8 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(double*)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetArgAddress(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( !dt->IsReference() && !dt->IsObjectHandle() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return (void*)*(asPWORD*)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetArgObject(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( !dt->IsObject() && !dt->IsFuncdef() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(void**)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetAddressOfArg(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // For object variables it's necessary to dereference the pointer to get the address of the value + if( !sysFunction->parameterTypes[arg].IsReference() && + sysFunction->parameterTypes[arg].IsObject() && + !sysFunction->parameterTypes[arg].IsObjectHandle() ) + return *(void**)&stackPointer[offset]; + + // Get the address of the value + return &stackPointer[offset]; +} + +// interface +int asCGeneric::GetArgTypeId(asUINT arg, asDWORD *flags) const +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + if( flags ) + { + *flags = sysFunction->inOutFlags[arg]; + *flags |= sysFunction->parameterTypes[arg].IsReadOnly() ? asTM_CONST : 0; + } + + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->GetTokenType() != ttQuestion ) + return engine->GetTypeIdFromDataType(*dt); + else + { + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Skip the actual value to get to the type id + offset += AS_PTR_SIZE; + + // Get the value + return stackPointer[offset]; + } +} + +// interface +int asCGeneric::SetReturnByte(asBYTE val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 1 ) + return asINVALID_TYPE; + + // Store the value + *(asBYTE*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnWord(asWORD val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 2 ) + return asINVALID_TYPE; + + // Store the value + *(asWORD*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnDWord(asDWORD val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 4 ) + return asINVALID_TYPE; + + // Store the value + *(asDWORD*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnQWord(asQWORD val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 2 ) + return asINVALID_TYPE; + + // Store the value + returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnFloat(float val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 1 ) + return asINVALID_TYPE; + + // Store the value + *(float*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnDouble(double val) +{ + // Verify the type of the return value + if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 2 ) + return asINVALID_TYPE; + + // Store the value + *(double*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnAddress(void *val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsReference() ) + { + // Store the value + *(void**)&returnVal = val; + return 0; + } + else if( sysFunction->returnType.IsObjectHandle() ) + { + // Store the handle without increasing reference + objectRegister = val; + return 0; + } + + return asINVALID_TYPE; +} + +// interface +int asCGeneric::SetReturnObject(void *obj) +{ + asCDataType *dt = &sysFunction->returnType; + if( !dt->IsObject() && !dt->IsFuncdef() ) + return asINVALID_TYPE; + + if( dt->IsReference() ) + { + *(void**)&returnVal = obj; + return 0; + } + + if( dt->IsObjectHandle() ) + { + // Increase the reference counter + if (dt->IsFuncdef()) + { + if (obj) + reinterpret_cast(obj)->AddRef(); + } + else + { + asSTypeBehaviour *beh = &CastToObjectType(dt->GetTypeInfo())->beh; + if (obj && beh && beh->addref) + engine->CallObjectMethod(obj, beh->addref); + } + } + else + { + // If function returns object by value the memory is already allocated. + // Here we should just initialize that memory by calling the copy constructor + // or the default constructor followed by the assignment operator + void *mem = (void*)*(asPWORD*)&stackPointer[-AS_PTR_SIZE]; + engine->ConstructScriptObjectCopy(mem, obj, CastToObjectType(dt->GetTypeInfo())); + return 0; + } + + objectRegister = obj; + + return 0; +} + +// internal +void *asCGeneric::GetReturnPointer() +{ + asCDataType &dt = sysFunction->returnType; + + if( (dt.IsObject() ||dt.IsFuncdef()) && !dt.IsReference() ) + { + // This function doesn't support returning on the stack but the use of + // the function doesn't require it so we don't need to implement it here. + asASSERT( !sysFunction->DoesReturnOnStack() ); + + return &objectRegister; + } + + return &returnVal; +} + +// interface +void *asCGeneric::GetAddressOfReturnLocation() +{ + asCDataType &dt = sysFunction->returnType; + + if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsReference() ) + { + if( sysFunction->DoesReturnOnStack() ) + { + // The memory is already preallocated on the stack, + // and the pointer to the location is found before the first arg + return (void*)*(asPWORD*)&stackPointer[-AS_PTR_SIZE]; + } + + // Reference types store the handle in the objectReference + return &objectRegister; + } + + // Primitive types and references are stored in the returnVal property + return &returnVal; +} + +// interface +int asCGeneric::GetReturnTypeId(asDWORD *flags) const +{ + return sysFunction->GetReturnTypeId(flags); +} + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_globalproperty.cpp b/3rdparty/angelscript/src/as_globalproperty.cpp new file mode 100644 index 0000000..3fe4627 --- /dev/null +++ b/3rdparty/angelscript/src/as_globalproperty.cpp @@ -0,0 +1,137 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + + +#include "as_config.h" +#include "as_property.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCGlobalProperty::asCGlobalProperty() +{ + memory = &storage; + memoryAllocated = false; + realAddress = 0; + initFunc = 0; + accessMask = 0xFFFFFFFF; + + refCount.set(1); +} + +asCGlobalProperty::~asCGlobalProperty() +{ +#ifndef WIP_16BYTE_ALIGNED + if( memoryAllocated ) { asDELETEARRAY(memory); } +#else + if( memoryAllocated ) { asDELETEARRAYALIGNED(memory); } +#endif + + if( initFunc ) + initFunc->ReleaseInternal(); +} + +void asCGlobalProperty::AddRef() +{ + refCount.atomicInc(); +} + +void asCGlobalProperty::Release() +{ + if( refCount.atomicDec() == 0 ) + asDELETE(this, asCGlobalProperty); +} + +void asCGlobalProperty::DestroyInternal() +{ + if( initFunc ) + { + initFunc->ReleaseInternal(); + initFunc = 0; + } +} + +void *asCGlobalProperty::GetAddressOfValue() +{ + return memory; +} + +// The global property structure is responsible for allocating the storage +// method for script declared variables. Each allocation is independent of +// other global properties, so that variables can be added and removed at +// any time. +void asCGlobalProperty::AllocateMemory() +{ + if( type.GetSizeOnStackDWords() > 2 ) + { +#ifndef WIP_16BYTE_ALIGNED + memory = asNEWARRAY(asDWORD, type.GetSizeOnStackDWords()); +#else + // TODO: Avoid aligned allocation if not needed to reduce the waste of memory for the alignment + memory = asNEWARRAYALIGNED(asDWORD, type.GetSizeOnStackDWords(), type.GetAlignment()); +#endif + memoryAllocated = true; + } +} + +void asCGlobalProperty::SetRegisteredAddress(void *p) +{ + realAddress = p; + if( type.IsObject() && !type.IsReference() && !type.IsObjectHandle() ) + { + // The global property is a pointer to a pointer + memory = &realAddress; + } + else + memory = p; +} + +void *asCGlobalProperty::GetRegisteredAddress() const +{ + return realAddress; +} + +void asCGlobalProperty::SetInitFunc(asCScriptFunction *in_initFunc) +{ + // This should only be done once + asASSERT( initFunc == 0 ); + + initFunc = in_initFunc; + initFunc->AddRefInternal(); +} + +asCScriptFunction *asCGlobalProperty::GetInitFunc() +{ + return initFunc; +} + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_memory.cpp b/3rdparty/angelscript/src/as_memory.cpp new file mode 100644 index 0000000..db3b06f --- /dev/null +++ b/3rdparty/angelscript/src/as_memory.cpp @@ -0,0 +1,276 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_memory.cpp +// +// Overload the default memory management functions so that we +// can let the application decide how to do it. +// + +#include + +#if !defined(__APPLE__) && !defined(__SNC__) && !defined(__ghs__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#endif + +#include "as_config.h" +#include "as_memory.h" +#include "as_scriptnode.h" +#include "as_bytecode.h" + +BEGIN_AS_NAMESPACE + +#ifdef WIP_16BYTE_ALIGN + +// TODO: Add support for 16byte aligned application types (e.g. __m128). The following is a list of things that needs to be implemented: +// +// ok - The script context must make sure to always allocate the local stack memory buffer on 16byte aligned boundaries (asCContext::ReserveStackSpace) +// ok - The engine must make sure to always allocate the memory for the script objects on 16byte aligned boundaries (asCScriptEngine::CallAlloc) +// ok - The application needs to inform a new flag when registering types that require 16byte alignment, e.g. asOBJ_APP_ALIGN16 (asCScriptEngine::RegisterObjectType) +// ok - The script object type must make sure to align member properties of these types correctly (asCObjectType::AddPropertyToClass) +// ok - Script global properties must allocate memory on 16byte boundaries if holding these types (asCGlobalProperty::AllocateMemory) +// TODO - The script compiler must make sure to allocate the local variables on 16byte boundaries (asCCompiler::AllocateVariable) +// TODO - The script compiler must add pad bytes on the stack for all function calls to guarantee that the stack position is 16byte aligned on entry in the called function (asCCompiler) +// TODO - The bytecode serializer must be capable of adjusting these pad bytes to guarantee platform independent saved bytecode. Remember that the registered type may not be 16byte aligned on all platforms (asCWriter & asCReader) +// TODO - The bytecode serializer must also be prepared to adjust the position of the local variables according to the need fro 16byte alignment (asCWriter & asCReader) +// TODO - The code for the native calling conventions must be adjusted for all platforms that should support 16byte aligned types (as_callfunc...) +// ok - When the context needs to grow the local stack memory it must copy the function arguments so that the stack entry position is 16byte aligned (asCContext::CallScriptFunction) +// TODO - When the context is prepared for a new call, it must set the initial stack position so the stack entry position is 16byte aligned (asCContext::Prepare) +// +// http://www.gamedev.net/topic/650555-alignment-requirements/ + + +// TODO: Allow user to register its own aligned memory routines +// Wrappers for aligned allocations +void *debugAlignedMalloc(size_t size, size_t align, const char *file, int line) +{ + void *mem = ((asALLOCFUNCDEBUG_t)userAlloc)(size + (align-1) + sizeof(void*), file, line); + + char *amem = ((char*)mem) + sizeof(void*); + if( (uintptr_t)amem & (align - 1) ) + amem += align - ((uintptr_t)amem & (align - 1)); + + ((void**)amem)[-1] = mem; + return amem; +} + +void *alignedMalloc(size_t size, size_t align) +{ + void *mem = userAlloc(size + (align-1) + sizeof(void*)); + + char *amem = ((char*)mem) + sizeof(void*); + if( (uintptr_t)amem & (align - 1) ) + amem += align - ((uintptr_t)amem & (align - 1)); + + ((void**)amem)[-1] = mem; + return amem; +} + +void alignedFree(void *mem) +{ + userFree( ((void**)mem)[-1] ); +} + +bool isAligned(const void* const pointer, asUINT alignment) +{ + return (uintptr_t(pointer) % alignment) == 0; +} +#endif + +// By default we'll use the standard memory management functions + +// Make sure these globals are initialized first. Otherwise the +// library may crash in case the application initializes the engine +// as a global variable. + +#ifdef _MSC_VER +// MSVC let's us choose between a couple of different initialization orders. +#pragma warning(disable: 4073) +#pragma init_seg(lib) +asALLOCFUNC_t userAlloc = malloc; +asFREEFUNC_t userFree = free; +#ifdef WIP_16BYTE_ALIGN +#ifdef AS_DEBUG +asALLOCALIGNEDFUNC_t userAllocAligned = (asALLOCALIGNEDFUNC_t)debugAlignedMalloc; +#else +asALLOCALIGNEDFUNC_t userAllocAligned = alignedMalloc; +#endif +asFREEALIGNEDFUNC_t userFreeAligned = alignedFree; +#endif +#else +// Other compilers will just have to rely on luck. +asALLOCFUNC_t userAlloc = malloc; +asFREEFUNC_t userFree = free; +#ifdef WIP_16BYTE_ALIGN +asALLOCALIGNEDFUNC_t userAllocAligned = alignedMalloc; +asFREEALIGNEDFUNC_t userFreeAligned = alignedFree; +#endif +#endif + +extern "C" +{ + +// interface +int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc) +{ + // Clean-up thread local memory before changing the allocation routines to avoid + // potential problem with trying to free memory using a different allocation + // routine than used when allocating it. + asThreadCleanup(); + + userAlloc = allocFunc; + userFree = freeFunc; + + return 0; +} + +// interface +int asResetGlobalMemoryFunctions() +{ + // Clean-up thread local memory before changing the allocation routines to avoid + // potential problem with trying to free memory using a different allocation + // routine than used when allocating it. + asThreadCleanup(); + + userAlloc = malloc; + userFree = free; + + return 0; +} + +// interface +void *asAllocMem(size_t size) +{ + return asNEWARRAY(asBYTE, size); +} + +// interface +void asFreeMem(void *mem) +{ + asDELETEARRAY(mem); +} + +} // extern "C" + +asCMemoryMgr::asCMemoryMgr() +{ +} + +asCMemoryMgr::~asCMemoryMgr() +{ + FreeUnusedMemory(); +} + +void asCMemoryMgr::FreeUnusedMemory() +{ + // It's necessary to protect the scriptNodePool from multiple + // simultaneous accesses, as the parser is used by several methods + // that can be executed simultaneously. + ENTERCRITICALSECTION(cs); + + int n; + for( n = 0; n < (signed)scriptNodePool.GetLength(); n++ ) + userFree(scriptNodePool[n]); + scriptNodePool.Allocate(0, false); + + LEAVECRITICALSECTION(cs); + + // The engine already protects against multiple threads + // compiling scripts simultaneously so this pool doesn't have + // to be protected again. + for( n = 0; n < (signed)byteInstructionPool.GetLength(); n++ ) + userFree(byteInstructionPool[n]); + byteInstructionPool.Allocate(0, false); +} + +void *asCMemoryMgr::AllocScriptNode() +{ + ENTERCRITICALSECTION(cs); + + if( scriptNodePool.GetLength() ) + { + void *tRet = scriptNodePool.PopLast(); + LEAVECRITICALSECTION(cs); + return tRet; + } + + LEAVECRITICALSECTION(cs); + +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(asCScriptNode), __FILE__, __LINE__); +#else + return userAlloc(sizeof(asCScriptNode)); +#endif +} + +void asCMemoryMgr::FreeScriptNode(void *ptr) +{ + ENTERCRITICALSECTION(cs); + + // Pre allocate memory for the array to avoid slow growth + if( scriptNodePool.GetLength() == 0 ) + scriptNodePool.Allocate(100, 0); + + scriptNodePool.PushLast(ptr); + + LEAVECRITICALSECTION(cs); +} + +#ifndef AS_NO_COMPILER + +void *asCMemoryMgr::AllocByteInstruction() +{ + if( byteInstructionPool.GetLength() ) + return byteInstructionPool.PopLast(); + +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(asCByteInstruction), __FILE__, __LINE__); +#else + return userAlloc(sizeof(asCByteInstruction)); +#endif +} + +void asCMemoryMgr::FreeByteInstruction(void *ptr) +{ + // Pre allocate memory for the array to avoid slow growth + if( byteInstructionPool.GetLength() == 0 ) + byteInstructionPool.Allocate(100, 0); + + byteInstructionPool.PushLast(ptr); +} + +#endif // AS_NO_COMPILER + +END_AS_NAMESPACE + + + diff --git a/3rdparty/angelscript/src/as_module.cpp b/3rdparty/angelscript/src/as_module.cpp new file mode 100644 index 0000000..9fe1a7d --- /dev/null +++ b/3rdparty/angelscript/src/as_module.cpp @@ -0,0 +1,1833 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_module.cpp +// +// A class that holds a script module +// + +#include "as_config.h" +#include "as_module.h" +#include "as_builder.h" +#include "as_context.h" +#include "as_texts.h" +#include "as_debug.h" +#include "as_restore.h" + +BEGIN_AS_NAMESPACE + + +// internal +asCModule::asCModule(const char *name, asCScriptEngine *engine) +{ + this->name = name; + this->engine = engine; + + userData = 0; + builder = 0; + isGlobalVarInitialized = false; + + accessMask = 1; + + defaultNamespace = engine->nameSpaces[0]; +} + +// internal +asCModule::~asCModule() +{ + InternalReset(); + + // The builder is not removed by InternalReset because it holds the script + // sections that will be built, so we need to explictly remove it now if it exists + if( builder ) + { + asDELETE(builder,asCBuilder); + builder = 0; + } + + if( engine ) + { + // Clean the user data + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n+1] ) + { + for( asUINT c = 0; c < engine->cleanModuleFuncs.GetLength(); c++ ) + if( engine->cleanModuleFuncs[c].type == userData[n] ) + engine->cleanModuleFuncs[c].cleanFunc(this); + } + } + + // Remove the module from the engine + ACQUIREEXCLUSIVE(engine->engineRWLock); + // The module must have been discarded before it is deleted + asASSERT( !engine->scriptModules.Exists(this) ); + engine->discardedModules.RemoveValue(this); + RELEASEEXCLUSIVE(engine->engineRWLock); + } +} + +// interface +void asCModule::Discard() +{ + // Reset the global variables already so that no object in the global variables keep the module alive forever. + // If any live object tries to access the global variables during clean up they will fail with a script exception, + // so the application must keep that in mind before discarding a module. + CallExit(); + + // Keep a local copy of the engine pointer, because once the module is moved do the discarded + // pile, it is possible that another thread might discard it while we are still in here. So no + // further access to members may be done after that + asCScriptEngine *lEngine = engine; + + // Instead of deleting the module immediately, move it to the discarded pile + // This will turn it invisible to the application, yet keep it alive until all + // external references to its entities have been released. + ACQUIREEXCLUSIVE(engine->engineRWLock); + if( lEngine->lastModule == this ) + lEngine->lastModule = 0; + lEngine->scriptModules.RemoveValue(this); + lEngine->discardedModules.PushLast(this); + RELEASEEXCLUSIVE(lEngine->engineRWLock); + + // Allow the engine to go over the list of discarded modules to see what can be cleaned up at this moment. + // Don't do this if the engine is already shutting down, as it will be done explicitly by the engine itself with error reporting + if( !lEngine->shuttingDown ) + { + if( lEngine->ep.autoGarbageCollect ) + lEngine->GarbageCollect(); + else + { + // GarbageCollect calls DeleteDiscardedModules, so no need + // to call it again if we already called GarbageCollect + lEngine->DeleteDiscardedModules(); + } + } +} + +// interface +void *asCModule::SetUserData(void *data, asPWORD type) +{ + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *oldData = reinterpret_cast(userData[n+1]); + userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return 0; +} + +// interface +void *asCModule::GetUserData(asPWORD type) const +{ + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engine->engineRWLock); + + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *ud = reinterpret_cast(userData[n+1]); + RELEASESHARED(engine->engineRWLock); + return ud; + } + } + + RELEASESHARED(engine->engineRWLock); + + return 0; +} + +// interface +asIScriptEngine *asCModule::GetEngine() const +{ + return engine; +} + +// interface +void asCModule::SetName(const char *in_name) +{ + name = in_name; +} + +// interface +const char *asCModule::GetName() const +{ + return name.AddressOf(); +} + +// interface +const char *asCModule::GetDefaultNamespace() const +{ + return defaultNamespace->name.AddressOf(); +} + +// interface +int asCModule::SetDefaultNamespace(const char *nameSpace) +{ + // TODO: cleanup: This function is similar to asCScriptEngine::SetDefaultNamespace. Can we reuse the code? + if( nameSpace == 0 ) + return asINVALID_ARG; + + asCString ns = nameSpace; + if( ns != "" ) + { + // Make sure the namespace is composed of alternating identifier and :: + size_t pos = 0; + bool expectIdentifier = true; + size_t len; + eTokenType t = ttIdentifier; + + for( ; pos < ns.GetLength(); pos += len ) + { + t = engine->tok.GetToken(ns.AddressOf() + pos, ns.GetLength() - pos, &len); + if( (expectIdentifier && t != ttIdentifier) || (!expectIdentifier && t != ttScope) ) + return asINVALID_DECLARATION; + + expectIdentifier = !expectIdentifier; + } + + // If the namespace ends with :: then strip it off + if( t == ttScope ) + ns.SetLength(ns.GetLength()-2); + } + + defaultNamespace = engine->AddNameSpace(ns.AddressOf()); + + return 0; +} + +// interface +int asCModule::AddScriptSection(const char *in_name, const char *in_code, size_t in_codeLength, int in_lineOffset) +{ +#ifdef AS_NO_COMPILER + UNUSED_VAR(in_name); + UNUSED_VAR(in_code); + UNUSED_VAR(in_codeLength); + UNUSED_VAR(in_lineOffset); + return asNOT_SUPPORTED; +#else + if( !builder ) + { + builder = asNEW(asCBuilder)(engine, this); + if( builder == 0 ) + return asOUT_OF_MEMORY; + } + + return builder->AddCode(in_name, in_code, (int)in_codeLength, in_lineOffset, (int)engine->GetScriptSectionNameIndex(in_name ? in_name : ""), engine->ep.copyScriptSections); +#endif +} + +// internal +void asCModule::JITCompile() +{ + asIJITCompiler *jit = engine->GetJITCompiler(); + if( !jit ) + return; + + for (unsigned int i = 0; i < scriptFunctions.GetLength(); i++) + scriptFunctions[i]->JITCompile(); +} + +// interface +int asCModule::Build() +{ +#ifdef AS_NO_COMPILER + return asNOT_SUPPORTED; +#else + TimeIt("asCModule::Build"); + + // Don't allow the module to be rebuilt if there are still + // external references that will need the previous code + // TODO: interface: The asIScriptModule must have a method for querying if the module is used + if( HasExternalReferences(false) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE); + return asMODULE_IS_IN_USE; + } + + // Only one thread may build at one time + // TODO: It should be possible to have multiple threads perform compilations + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + InternalReset(); + + if( !builder ) + { + engine->BuildCompleted(); + return asSUCCESS; + } + + // Compile the script + r = builder->Build(); + asDELETE(builder,asCBuilder); + builder = 0; + + if( r < 0 ) + { + // Reset module again + InternalReset(); + + engine->BuildCompleted(); + return r; + } + + JITCompile(); + + engine->PrepareEngine(); + +#ifdef AS_DEBUG + // Verify that there are no unwanted gaps in the scriptFunctions array. + for( asUINT n = 1; n < engine->scriptFunctions.GetLength(); n++ ) + { + int id = n; + if( engine->scriptFunctions[n] == 0 && !engine->freeScriptFunctionIds.Exists(id) ) + asASSERT( false ); + } +#endif + + engine->BuildCompleted(); + + // Initialize global variables + if( r >= 0 && engine->ep.initGlobalVarsAfterBuild ) + r = ResetGlobalVars(0); + + return r; +#endif +} + +// interface +int asCModule::ResetGlobalVars(asIScriptContext *ctx) +{ + if( isGlobalVarInitialized ) + CallExit(); + + return CallInit(ctx); +} + +// interface +asIScriptFunction *asCModule::GetFunctionByIndex(asUINT index) const +{ + return const_cast(globalFunctions.Get(index)); +} + +// internal +int asCModule::CallInit(asIScriptContext *myCtx) +{ + if( isGlobalVarInitialized ) + return asERROR; + + // Each global variable needs to be cleared individually + asCSymbolTableIterator it = scriptGlobals.List(); + while( it ) + { + asCGlobalProperty *desc = *it; + memset(desc->GetAddressOfValue(), 0, sizeof(asDWORD)*desc->type.GetSizeOnStackDWords()); + it++; + } + + // Call the init function for each of the global variables + asIScriptContext *ctx = myCtx; + int r = asEXECUTION_FINISHED; + it = scriptGlobals.List(); + while( it && r == asEXECUTION_FINISHED ) + { + asCGlobalProperty *desc = *it; + it++; + if( desc->GetInitFunc() ) + { + if( ctx == 0 ) + { + ctx = engine->RequestContext(); + if( ctx == 0 ) + break; + } + + r = ctx->Prepare(desc->GetInitFunc()); + if( r >= 0 ) + { + r = ctx->Execute(); + if( r != asEXECUTION_FINISHED ) + { + asCString msg; + msg.Format(TXT_FAILED_TO_INITIALIZE_s, desc->name.AddressOf()); + asCScriptFunction *func = desc->GetInitFunc(); + + engine->WriteMessage(func->scriptData->scriptSectionIdx >= 0 ? engine->scriptSectionNames[func->scriptData->scriptSectionIdx]->AddressOf() : "", + func->GetLineNumber(0, 0) & 0xFFFFF, + func->GetLineNumber(0, 0) >> 20, + asMSGTYPE_ERROR, + msg.AddressOf()); + + if( r == asEXECUTION_EXCEPTION ) + { + const asIScriptFunction *function = ctx->GetExceptionFunction(); + + msg.Format(TXT_EXCEPTION_s_IN_s, ctx->GetExceptionString(), function->GetDeclaration()); + + engine->WriteMessage(function->GetScriptSectionName(), + ctx->GetExceptionLineNumber(), + 0, + asMSGTYPE_INFORMATION, + msg.AddressOf()); + } + } + } + } + } + + if( ctx && !myCtx ) + { + engine->ReturnContext(ctx); + ctx = 0; + } + + // Even if the initialization failed we need to set the + // flag that the variables have been initialized, otherwise + // the module won't free those variables that really were + // initialized. + isGlobalVarInitialized = true; + + if( r != asEXECUTION_FINISHED ) + return asINIT_GLOBAL_VARS_FAILED; + + return asSUCCESS; +} + +// internal +void asCModule::CallExit() +{ + if( !isGlobalVarInitialized ) return; + + asCSymbolTableIterator it = scriptGlobals.List(); + while( it ) + { + if( (*it)->type.IsObject() ) + { + void **obj = (void**)(*it)->GetAddressOfValue(); + if( *obj ) + { + asCObjectType *ot = CastToObjectType((*it)->type.GetTypeInfo()); + + if( ot->flags & asOBJ_REF ) + { + asASSERT( (ot->flags & asOBJ_NOCOUNT) || ot->beh.release ); + if( ot->beh.release ) + engine->CallObjectMethod(*obj, ot->beh.release); + } + else + { + if( ot->beh.destruct ) + engine->CallObjectMethod(*obj, ot->beh.destruct); + + engine->CallFree(*obj); + } + + // Set the address to 0 as someone might try to access the variable afterwards + *obj = 0; + } + } + else if ((*it)->type.IsFuncdef()) + { + asCScriptFunction **func = (asCScriptFunction**)(*it)->GetAddressOfValue(); + if (*func) + { + (*func)->Release(); + *func = 0; + } + } + it++; + } + + isGlobalVarInitialized = false; +} + +// internal +bool asCModule::HasExternalReferences(bool shuttingDown) +{ + // Check all entiteis in the module for any external references. + // If there are any external references the module cannot be deleted yet. + + asCSymbolTableIterator it = scriptGlobals.List(); + while (it) + { + asCGlobalProperty *desc = *it; + if (desc->GetInitFunc() && desc->GetInitFunc()->externalRefCount.get()) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + // TODO: Use a better error message + asCString tmpName = "init " + desc->name; + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, tmpName.AddressOf(), desc->GetInitFunc()->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + it++; + } + + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + if( scriptFunctions[n] && scriptFunctions[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, scriptFunctions[n]->GetName(), scriptFunctions[n]->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < classTypes.GetLength(); n++ ) + if( classTypes[n] && classTypes[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_TYPE_IS_NAMED_s, classTypes[n]->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < funcDefs.GetLength(); n++ ) + if( funcDefs[n] && funcDefs[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, funcDefs[n]->GetName(), funcDefs[n]->funcdef->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < templateInstances.GetLength(); n++ ) + if( templateInstances[n] && templateInstances[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_TYPE_IS_NAMED_s, templateInstances[n]->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + return false; +} + +// internal +void asCModule::InternalReset() +{ + CallExit(); + + asUINT n; + + // Remove all global functions + globalFunctions.Clear(); + + // Destroy the internals of the global properties here, but do not yet remove them from the + // engine, because functions need the engine's varAddressMap to get to the property. If the + // property is removed already, it may leak as the refCount doesn't reach 0. + asCSymbolTableIterator globIt = scriptGlobals.List(); + while( globIt ) + { + (*globIt)->DestroyInternal(); + globIt++; + } + + UnbindAllImportedFunctions(); + + // Free bind information + for( n = 0; n < bindInformations.GetLength(); n++ ) + { + if( bindInformations[n] ) + { + bindInformations[n]->importedFunctionSignature->ReleaseInternal(); + + asDELETE(bindInformations[n], sBindInfo); + } + } + bindInformations.SetLength(0); + + // Free declared types, including classes, typedefs, and enums + for( n = 0; n < templateInstances.GetLength(); n++ ) + { + asCObjectType *type = templateInstances[n]; + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + + // Orphan the template instance + type->module = 0; + + // No other module is holding the template type + engine->RemoveTemplateInstanceType(type); + type->ReleaseInternal(); + } + templateInstances.SetLength(0); + for( n = 0; n < classTypes.GetLength(); n++ ) + { + asCObjectType *type = classTypes[n]; + if( type->IsShared() ) + { + // The type is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + } + + // The type should be destroyed now + type->DestroyInternal(); + + // Remove the type from the engine + if( type->IsShared() ) + { + engine->sharedScriptTypes.RemoveValue(type); + type->ReleaseInternal(); + } + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } + classTypes.SetLength(0); + for( n = 0; n < enumTypes.GetLength(); n++ ) + { + asCEnumType *type = enumTypes[n]; + if( type->IsShared() ) + { + // The type is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + } + + // Remove the type from the engine + if( type->IsShared() ) + { + engine->sharedScriptTypes.RemoveValue(type); + type->ReleaseInternal(); + } + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } + enumTypes.SetLength(0); + for( n = 0; n < typeDefs.GetLength(); n++ ) + { + asCTypedefType *type = typeDefs[n]; + + // The type should be destroyed now + type->DestroyInternal(); + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } + typeDefs.SetLength(0); + + // Free funcdefs + for( n = 0; n < funcDefs.GetLength(); n++ ) + { + asCFuncdefType *func = funcDefs[n]; + asASSERT(func); + if( func->funcdef && func->funcdef->IsShared() ) + { + // The funcdef is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedType(func, this) != this ) + { + // The funcdef is owned by another module, just release our reference + func->ReleaseInternal(); + continue; + } + } + + func->DestroyInternal(); + engine->RemoveFuncdef(func); + func->module = 0; + func->ReleaseInternal(); + } + funcDefs.SetLength(0); + + // Then release the functions + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + asCScriptFunction *func = scriptFunctions[n]; + if( func->IsShared() ) + { + // The func is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedFunc(func, this) != this ) + { + // The func is owned by another module, just release our reference + func->ReleaseInternal(); + continue; + } + } + + func->DestroyInternal(); + func->module = 0; + func->ReleaseInternal(); + } + scriptFunctions.SetLength(0); + + // Now remove and release the global properties as there are no more references to them + globIt = scriptGlobals.List(); + while( globIt ) + { + engine->RemoveGlobalProperty(*globIt); + asASSERT( (*globIt)->refCount.get() == 1 ); + (*globIt)->Release(); + globIt++; + } + scriptGlobals.Clear(); + + asASSERT( IsEmpty() ); +} + +// interface +asIScriptFunction *asCModule::GetFunctionByName(const char *in_name) const +{ + asSNameSpace *ns = defaultNamespace; + while( ns ) + { + const asCArray &idxs = globalFunctions.GetIndexes(ns, in_name); + if( idxs.GetLength() != 1 ) + return 0; + + const asIScriptFunction *func = globalFunctions.Get(idxs[0]); + if( func ) + return const_cast(func); + + // Recursively search parent namespaces + ns = engine->GetParentNameSpace(ns); + } + + return 0; +} + +// interface +asUINT asCModule::GetImportedFunctionCount() const +{ + return (asUINT)bindInformations.GetLength(); +} + +// interface +int asCModule::GetImportedFunctionIndexByDecl(const char *decl) const +{ + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCScriptFunction func(engine, const_cast(this), asFUNC_DUMMY); + bld.ParseFunctionDeclaration(0, decl, &func, false, 0, 0, defaultNamespace); + + // TODO: optimize: Improve linear search + // Search script functions for matching interface + int id = -1; + for( asUINT n = 0; n < bindInformations.GetLength(); ++n ) + { + if( func.name == bindInformations[n]->importedFunctionSignature->name && + func.returnType == bindInformations[n]->importedFunctionSignature->returnType && + func.parameterTypes.GetLength() == bindInformations[n]->importedFunctionSignature->parameterTypes.GetLength() ) + { + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) + { + if( func.parameterTypes[p] != bindInformations[n]->importedFunctionSignature->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( id == -1 ) + id = n; + else + return asMULTIPLE_FUNCTIONS; + } + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + +// interface +asUINT asCModule::GetFunctionCount() const +{ + return (asUINT)globalFunctions.GetSize(); +} + +// interface +asIScriptFunction *asCModule::GetFunctionByDecl(const char *decl) const +{ + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCScriptFunction func(engine, const_cast(this), asFUNC_DUMMY); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false, 0, 0, defaultNamespace); + if( r < 0 ) + { + // Invalid declaration + // TODO: Write error to message stream + return 0; + } + + // Use the defaultNamespace implicitly unless an explicit namespace has been provided + asSNameSpace *ns = func.nameSpace == engine->nameSpaces[0] ? defaultNamespace : func.nameSpace; + + // Search script functions for matching interface + while( ns ) + { + asIScriptFunction *f = 0; + const asCArray &idxs = globalFunctions.GetIndexes(ns, func.name); + for( unsigned int n = 0; n < idxs.GetLength(); n++ ) + { + const asCScriptFunction *funcPtr = globalFunctions.Get(idxs[n]); + if( funcPtr->objectType == 0 && + func.returnType == funcPtr->returnType && + func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() + ) + { + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) + { + if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( f == 0 ) + f = const_cast(funcPtr); + else + // Multiple functions + return 0; + } + } + } + + if( f ) + return f; + else + { + // Search for matching functions in the parent namespace + ns = engine->GetParentNameSpace(ns); + } + } + + return 0; +} + +// interface +asUINT asCModule::GetGlobalVarCount() const +{ + return (asUINT)scriptGlobals.GetSize(); +} + +// interface +int asCModule::GetGlobalVarIndexByName(const char *in_name) const +{ + asSNameSpace *ns = defaultNamespace; + + // Find the global var id + while( ns ) + { + int id = scriptGlobals.GetFirstIndex(ns, in_name); + if( id >= 0 ) return id; + + // Recursively search parent namespaces + ns = engine->GetParentNameSpace(ns); + } + + return asNO_GLOBAL_VAR; +} + +// interface +int asCModule::RemoveGlobalVar(asUINT index) +{ + asCGlobalProperty *prop = scriptGlobals.Get(index); + if( !prop ) + return asINVALID_ARG; + + // Destroy the internal of the global variable (removes the initialization function) + prop->DestroyInternal(); + + // Check if the module is the only one referring to the module, if so remove it from the engine too + // If the property is not removed now, it will be removed later when the module is discarded + if( prop->refCount.get() == 2 ) + engine->RemoveGlobalProperty(prop); + + // Remove the global variable from the module + scriptGlobals.Erase(index); + prop->Release(); + + return 0; +} + +// interface +int asCModule::GetGlobalVarIndexByDecl(const char *decl) const +{ + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCString declName; + asSNameSpace *nameSpace; + asCDataType dt; + int r = bld.ParseVariableDeclaration(decl, defaultNamespace, declName, nameSpace, dt); + if( r < 0 ) + return r; + + // Search global variables for a match + while( nameSpace ) + { + int id = scriptGlobals.GetFirstIndex(nameSpace, declName, asCCompGlobPropType(dt)); + if( id != -1 ) + return id; + + // Recursively search parent namespace + nameSpace = engine->GetParentNameSpace(nameSpace); + } + + return asNO_GLOBAL_VAR; +} + +// interface +void *asCModule::GetAddressOfGlobalVar(asUINT index) +{ + asCGlobalProperty *prop = scriptGlobals.Get(index); + if( !prop ) + return 0; + + // For object variables it's necessary to dereference the pointer to get the address of the value + if( prop->type.IsObject() && + !prop->type.IsObjectHandle() ) + return *(void**)(prop->GetAddressOfValue()); + + return (void*)(prop->GetAddressOfValue()); +} + +// interface +const char *asCModule::GetGlobalVarDeclaration(asUINT index, bool includeNamespace) const +{ + const asCGlobalProperty *prop = scriptGlobals.Get(index); + if (!prop) return 0; + + asCString *tempString = &asCThreadManager::GetLocalData()->string; + *tempString = prop->type.Format(defaultNamespace); + *tempString += " "; + if( includeNamespace && prop->nameSpace->name != "" ) + *tempString += prop->nameSpace->name + "::"; + *tempString += prop->name; + + return tempString->AddressOf(); +} + +// interface +int asCModule::GetGlobalVar(asUINT index, const char **out_name, const char **out_nameSpace, int *out_typeId, bool *out_isConst) const +{ + const asCGlobalProperty *prop = scriptGlobals.Get(index); + if (!prop) return 0; + + if( out_name ) + *out_name = prop->name.AddressOf(); + if( out_nameSpace ) + *out_nameSpace = prop->nameSpace->name.AddressOf(); + if( out_typeId ) + *out_typeId = engine->GetTypeIdFromDataType(prop->type); + if( out_isConst ) + *out_isConst = prop->type.IsReadOnly(); + + return asSUCCESS; +} + +// interface +asUINT asCModule::GetObjectTypeCount() const +{ + return (asUINT)classTypes.GetLength(); +} + +// interface +asITypeInfo *asCModule::GetObjectTypeByIndex(asUINT index) const +{ + if( index >= classTypes.GetLength() ) + return 0; + + return classTypes[index]; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +asITypeInfo *asCModule::GetObjectTypeByName(const char *in_name) const +{ + asITypeInfo *ti = GetTypeInfoByName(in_name); + return CastToObjectType(reinterpret_cast(ti)); +} +#endif + +// interface +asITypeInfo *asCModule::GetTypeInfoByName(const char *in_name) const +{ + asSNameSpace *ns = defaultNamespace; + while (ns) + { + for (asUINT n = 0; n < classTypes.GetLength(); n++) + { + if (classTypes[n] && + classTypes[n]->name == in_name && + classTypes[n]->nameSpace == ns) + return classTypes[n]; + } + + for (asUINT n = 0; n < enumTypes.GetLength(); n++) + { + if (enumTypes[n] && + enumTypes[n]->name == in_name && + enumTypes[n]->nameSpace == ns) + return enumTypes[n]; + } + + for (asUINT n = 0; n < typeDefs.GetLength(); n++) + { + if (typeDefs[n] && + typeDefs[n]->name == in_name && + typeDefs[n]->nameSpace == ns) + return typeDefs[n]; + } + + // Recursively search parent namespace + ns = engine->GetParentNameSpace(ns); + } + + return 0; +} + +// interface +int asCModule::GetTypeIdByDecl(const char *decl) const +{ + asCDataType dt; + + // This const cast is safe since we know the engine won't be modified + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if( r < 0 ) + return asINVALID_TYPE; + + return engine->GetTypeIdFromDataType(dt); +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +asITypeInfo *asCModule::GetObjectTypeByDecl(const char *decl) const +{ + asITypeInfo *ti = GetTypeInfoByDecl(decl); + return CastToObjectType(reinterpret_cast(ti)); +} +#endif + +// interface +asITypeInfo *asCModule::GetTypeInfoByDecl(const char *decl) const +{ + asCDataType dt; + + // This const cast is safe since we know the engine won't be modified + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if (r < 0) + return 0; + + return dt.GetTypeInfo(); +} + +// interface +asUINT asCModule::GetEnumCount() const +{ + return enumTypes.GetLength(); +} + +// interface +asITypeInfo *asCModule::GetEnumByIndex(asUINT index) const +{ + if( index >= enumTypes.GetLength() ) + return 0; + + return enumTypes[index]; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +int asCModule::GetEnumValueCount(int enumTypeId) const +{ + asITypeInfo *ti = engine->GetTypeInfoById(enumTypeId); + asCEnumType *e = CastToEnumType(reinterpret_cast(ti)); + if (e == 0) + return asINVALID_TYPE; + + return e->GetEnumValueCount(); +} +#endif + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +const char *asCModule::GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const +{ + asITypeInfo *ti = engine->GetTypeInfoById(enumTypeId); + asCEnumType *e = CastToEnumType(reinterpret_cast(ti)); + if (e == 0) + return 0; + + return e->GetEnumValueByIndex(index, outValue); +} +#endif + +// interface +asUINT asCModule::GetTypedefCount() const +{ + return (asUINT)typeDefs.GetLength(); +} + +// interface +asITypeInfo *asCModule::GetTypedefByIndex(asUINT index) const +{ + if( index >= typeDefs.GetLength() ) + return 0; + + return typeDefs[index]; +} + +// internal +int asCModule::GetNextImportedFunctionId() +{ + // TODO: multithread: This will break if one thread if freeing a module, while another is being compiled + if( engine->freeImportedFunctionIdxs.GetLength() ) + return FUNC_IMPORTED | (asUINT)engine->freeImportedFunctionIdxs[engine->freeImportedFunctionIdxs.GetLength()-1]; + + return FUNC_IMPORTED | (asUINT)engine->importedFunctions.GetLength(); +} + +#ifndef AS_NO_COMPILER +// internal +int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &funcName, const asCDataType &returnType, const asCArray ¶ms, const asCArray ¶mNames, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType, bool isConstMethod, bool isGlobalFunction, bool isPrivate, bool isProtected, bool isFinal, bool isOverride, bool isShared, asSNameSpace *ns) +{ + asASSERT(id >= 0); + + // Store the function information + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, this, isInterface ? asFUNC_INTERFACE : asFUNC_SCRIPT); + if( func == 0 ) + { + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return asOUT_OF_MEMORY; + } + + if( ns == 0 ) + ns = engine->nameSpaces[0]; + + // All methods of shared objects are also shared + if( objType && objType->IsShared() ) + isShared = true; + + func->name = funcName; + func->nameSpace = ns; + func->id = id; + func->returnType = returnType; + if( func->funcType == asFUNC_SCRIPT ) + { + func->scriptData->scriptSectionIdx = sectionIdx; + func->scriptData->declaredAt = declaredAt; + } + func->parameterTypes = params; + func->parameterNames = paramNames; + func->inOutFlags = inOutFlags; + func->defaultArgs = defaultArgs; + func->objectType = objType; + if( objType ) + objType->AddRefInternal(); + func->isReadOnly = isConstMethod; + func->isPrivate = isPrivate; + func->isProtected = isProtected; + func->isFinal = isFinal; + func->isOverride = isOverride; + func->isShared = isShared; + + asASSERT( params.GetLength() == inOutFlags.GetLength() && params.GetLength() == defaultArgs.GetLength() ); + + // Verify that we are not assigning either the final or override specifier(s) if we are registering a non-member function + asASSERT( !(!objType && isFinal) ); + asASSERT( !(!objType && isOverride) ); + + // The internal ref count was already set by the constructor + scriptFunctions.PushLast(func); + engine->AddScriptFunction(func); + + // Compute the signature id + if( objType ) + func->ComputeSignatureId(); + + // Add reference + if( isGlobalFunction ) + globalFunctions.Put(func); + + return 0; +} + +// internal +int asCModule::AddScriptFunction(asCScriptFunction *func) +{ + scriptFunctions.PushLast(func); + func->AddRefInternal(); + engine->AddScriptFunction(func); + + // If the function that is being added is an already compiled shared function + // then it is necessary to look for anonymous functions that may be declared + // within it and add those as well + if( func->isShared && func->funcType == asFUNC_SCRIPT ) + { + // Loop through the byte code and check all the + // asBC_FuncPtr instructions for anonymous functions + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength(); + for( asUINT n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + if( c == asBC_FuncPtr ) + { + asCScriptFunction *f = reinterpret_cast(asBC_PTRARG(&bc[n])); + // Anonymous functions start with $ + // There are never two equal anonymous functions so it is not necessary to look for duplicates + if( f && f->name[0] == '$' ) + { + AddScriptFunction(f); + globalFunctions.Put(f); + } + } + n += asBCTypeSize[asBCInfo[c].type]; + } + } + + return 0; +} + +// internal +int asCModule::AddImportedFunction(int id, const asCString &funcName, const asCDataType &returnType, const asCArray ¶ms, const asCArray &inOutFlags, const asCArray &defaultArgs, asSNameSpace *ns, const asCString &moduleName) +{ + asASSERT(id >= 0); + + // Store the function information + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, this, asFUNC_IMPORTED); + if( func == 0 ) + { + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return asOUT_OF_MEMORY; + } + + func->name = funcName; + func->id = id; + func->returnType = returnType; + func->nameSpace = ns; + func->parameterTypes = params; + func->inOutFlags = inOutFlags; + func->defaultArgs = defaultArgs; + func->objectType = 0; + + sBindInfo *info = asNEW(sBindInfo); + if( info == 0 ) + { + asDELETE(func, asCScriptFunction); + return asOUT_OF_MEMORY; + } + + info->importedFunctionSignature = func; + info->boundFunctionId = -1; + info->importFromModule = moduleName; + bindInformations.PushLast(info); + + // Add the info to the array in the engine + if( engine->freeImportedFunctionIdxs.GetLength() ) + engine->importedFunctions[engine->freeImportedFunctionIdxs.PopLast()] = info; + else + engine->importedFunctions.PushLast(info); + + return 0; +} +#endif + +// internal +asCScriptFunction *asCModule::GetImportedFunction(int index) const +{ + return bindInformations[index]->importedFunctionSignature; +} + +// interface +int asCModule::BindImportedFunction(asUINT index, asIScriptFunction *func) +{ + // First unbind the old function + int r = UnbindImportedFunction(index); + if( r < 0 ) return r; + + // Must verify that the interfaces are equal + asCScriptFunction *dst = GetImportedFunction(index); + if( dst == 0 ) return asNO_FUNCTION; + + if( func == 0 ) + return asINVALID_ARG; + + asCScriptFunction *src = engine->GetScriptFunction(func->GetId()); + if( src == 0 ) + return asNO_FUNCTION; + + // Verify return type + if( dst->returnType != src->returnType ) + return asINVALID_INTERFACE; + + if( dst->parameterTypes.GetLength() != src->parameterTypes.GetLength() ) + return asINVALID_INTERFACE; + + for( asUINT n = 0; n < dst->parameterTypes.GetLength(); ++n ) + { + if( dst->parameterTypes[n] != src->parameterTypes[n] ) + return asINVALID_INTERFACE; + } + + bindInformations[index]->boundFunctionId = src->GetId(); + src->AddRefInternal(); + + return asSUCCESS; +} + +// interface +int asCModule::UnbindImportedFunction(asUINT index) +{ + if( index >= bindInformations.GetLength() ) + return asINVALID_ARG; + + // Remove reference to old module + if( bindInformations[index] ) + { + int oldFuncID = bindInformations[index]->boundFunctionId; + if( oldFuncID != -1 ) + { + bindInformations[index]->boundFunctionId = -1; + engine->scriptFunctions[oldFuncID]->ReleaseInternal(); + } + } + + return asSUCCESS; +} + +// interface +const char *asCModule::GetImportedFunctionDeclaration(asUINT index) const +{ + asCScriptFunction *func = GetImportedFunction(index); + if( func == 0 ) return 0; + + asCString *tempString = &asCThreadManager::GetLocalData()->string; + *tempString = func->GetDeclarationStr(); + + return tempString->AddressOf(); +} + +// interface +const char *asCModule::GetImportedFunctionSourceModule(asUINT index) const +{ + if( index >= bindInformations.GetLength() ) + return 0; + + return bindInformations[index]->importFromModule.AddressOf(); +} + +// inteface +int asCModule::BindAllImportedFunctions() +{ + bool notAllFunctionsWereBound = false; + + // Bind imported functions + int c = GetImportedFunctionCount(); + for( int n = 0; n < c; ++n ) + { + asCScriptFunction *importFunc = GetImportedFunction(n); + if( importFunc == 0 ) return asERROR; + + asCString str = importFunc->GetDeclarationStr(false, true); + + // Get module name from where the function should be imported + const char *moduleName = GetImportedFunctionSourceModule(n); + if( moduleName == 0 ) return asERROR; + + asCModule *srcMod = engine->GetModule(moduleName, false); + asIScriptFunction *func = 0; + if( srcMod ) + func = srcMod->GetFunctionByDecl(str.AddressOf()); + + if( func == 0 ) + notAllFunctionsWereBound = true; + else + { + if( BindImportedFunction(n, func) < 0 ) + notAllFunctionsWereBound = true; + } + } + + if( notAllFunctionsWereBound ) + return asCANT_BIND_ALL_FUNCTIONS; + + return asSUCCESS; +} + +// interface +int asCModule::UnbindAllImportedFunctions() +{ + asUINT c = GetImportedFunctionCount(); + for( asUINT n = 0; n < c; ++n ) + UnbindImportedFunction(n); + + return asSUCCESS; +} + +// internal +asCTypeInfo *asCModule::GetType(const char *type, asSNameSpace *ns) +{ + asUINT n; + + // TODO: optimize: Improve linear search + for (n = 0; n < classTypes.GetLength(); n++) + if (classTypes[n]->name == type && + classTypes[n]->nameSpace == ns) + return classTypes[n]; + + for (n = 0; n < enumTypes.GetLength(); n++) + if (enumTypes[n]->name == type && + enumTypes[n]->nameSpace == ns) + return enumTypes[n]; + + for (n = 0; n < typeDefs.GetLength(); n++) + if (typeDefs[n]->name == type && + typeDefs[n]->nameSpace == ns) + return typeDefs[n]; + + for (n = 0; n < funcDefs.GetLength(); n++) + if (funcDefs[n]->name == type && + funcDefs[n]->nameSpace == ns) + return funcDefs[n]; + + return 0; +} + +// internal +asCObjectType *asCModule::GetObjectType(const char *type, asSNameSpace *ns) +{ + asUINT n; + + // TODO: optimize: Improve linear search + for( n = 0; n < classTypes.GetLength(); n++ ) + if( classTypes[n]->name == type && + classTypes[n]->nameSpace == ns ) + return classTypes[n]; + + return 0; +} + +// internal +asCGlobalProperty *asCModule::AllocateGlobalProperty(const char *propName, const asCDataType &dt, asSNameSpace *ns) +{ + asCGlobalProperty *prop = engine->AllocateGlobalProperty(); + prop->name = propName; + prop->nameSpace = ns; + + // Allocate the memory for this property based on its type + prop->type = dt; + prop->AllocateMemory(); + + // Make an entry in the address to variable map + engine->varAddressMap.Insert(prop->GetAddressOfValue(), prop); + + // Store the variable in the module scope + scriptGlobals.Put(prop); + prop->AddRef(); + + return prop; +} + +// internal +bool asCModule::IsEmpty() const +{ + if( scriptFunctions.GetLength() ) return false; + if( globalFunctions.GetSize() ) return false; + if( bindInformations.GetLength() ) return false; + if( scriptGlobals.GetSize() ) return false; + if( classTypes.GetLength() ) return false; + if( enumTypes.GetLength() ) return false; + if( typeDefs.GetLength() ) return false; + if( funcDefs.GetLength() ) return false; + + return true; +} + +// interface +int asCModule::SaveByteCode(asIBinaryStream *out, bool stripDebugInfo) const +{ +#ifdef AS_NO_COMPILER + UNUSED_VAR(out); + UNUSED_VAR(stripDebugInfo); + return asNOT_SUPPORTED; +#else + if( out == 0 ) return asINVALID_ARG; + + // Make sure there is actually something to save + if( IsEmpty() ) + return asERROR; + + asCWriter write(const_cast(this), out, engine, stripDebugInfo); + return write.Write(); +#endif +} + +// interface +int asCModule::LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped) +{ + if( in == 0 ) return asINVALID_ARG; + + // Don't allow the module to be rebuilt if there are still + // external references that will need the previous code + if( HasExternalReferences(false) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE); + return asMODULE_IS_IN_USE; + } + + // Only permit loading bytecode if no other thread is currently compiling + // TODO: It should be possible to have multiple threads perform compilations + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + asCReader read(this, in, engine); + r = read.Read(wasDebugInfoStripped); + if (r < 0) + { + engine->BuildCompleted(); + return r; + } + + JITCompile(); + +#ifdef AS_DEBUG + // Verify that there are no unwanted gaps in the scriptFunctions array. + for( asUINT n = 1; n < engine->scriptFunctions.GetLength(); n++ ) + { + int id = n; + if( engine->scriptFunctions[n] == 0 && !engine->freeScriptFunctionIds.Exists(id) ) + asASSERT( false ); + } +#endif + + engine->BuildCompleted(); + + return r; +} + +// interface +int asCModule::CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) +{ +#ifdef AS_NO_COMPILER + UNUSED_VAR(sectionName); + UNUSED_VAR(code); + UNUSED_VAR(lineOffset); + return asNOT_SUPPORTED; +#else + // Validate arguments + if( code == 0 ) + return asINVALID_ARG; + + // Only one thread may build at one time + // TODO: It should be possible to have multiple threads perform compilations + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + // Prepare the engine + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + // Compile the global variable and add it to the module scope + asCBuilder varBuilder(engine, this); + asCString str = code; + r = varBuilder.CompileGlobalVar(sectionName, str.AddressOf(), lineOffset); + + engine->BuildCompleted(); + + // Initialize the variable + if( r >= 0 && engine->ep.initGlobalVarsAfterBuild ) + { + // Clear the memory + asCGlobalProperty *prop = scriptGlobals.GetLast(); + if( prop ) + { + memset(prop->GetAddressOfValue(), 0, sizeof(asDWORD)*prop->type.GetSizeOnStackDWords()); + + if( prop->GetInitFunc() ) + { + // Call the init function for the global variable + asIScriptContext *ctx = 0; + r = engine->CreateContext(&ctx, true); + if( r < 0 ) + return r; + + r = ctx->Prepare(prop->GetInitFunc()); + if( r >= 0 ) + r = ctx->Execute(); + + ctx->Release(); + } + } + } + + return r; +#endif +} + +// interface +int asCModule::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) +{ + // Make sure the outFunc is null if the function fails, so the + // application doesn't attempt to release a non-existent function + if( outFunc ) + *outFunc = 0; + +#ifdef AS_NO_COMPILER + UNUSED_VAR(sectionName); + UNUSED_VAR(code); + UNUSED_VAR(lineOffset); + UNUSED_VAR(compileFlags); + return asNOT_SUPPORTED; +#else + // Validate arguments + if( code == 0 || + (compileFlags != 0 && compileFlags != asCOMP_ADD_TO_MODULE) ) + return asINVALID_ARG; + + // Only one thread may build at one time + // TODO: It should be possible to have multiple threads perform compilations + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + // Prepare the engine + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + // Compile the single function + asCBuilder funcBuilder(engine, this); + asCString str = code; + asCScriptFunction *func = 0; + r = funcBuilder.CompileFunction(sectionName, str.AddressOf(), lineOffset, compileFlags, &func); + + engine->BuildCompleted(); + + if( r >= 0 && outFunc && func ) + { + // Return the function to the caller and add an external reference + *outFunc = func; + func->AddRef(); + } + + // Release our reference to the function + if( func ) + func->ReleaseInternal(); + + return r; +#endif +} + +// interface +int asCModule::RemoveFunction(asIScriptFunction *func) +{ + // Find the global function + asCScriptFunction *f = static_cast(func); + int idx = globalFunctions.GetIndex(f); + if( idx >= 0 ) + { + globalFunctions.Erase(idx); + scriptFunctions.RemoveValue(f); + f->ReleaseInternal(); + return 0; + } + + return asNO_FUNCTION; +} + +#ifndef AS_NO_COMPILER +// internal +int asCModule::AddFuncDef(const asCString &funcName, asSNameSpace *ns, asCObjectType *parent) +{ + // namespace and parent are mutually exclusive + asASSERT((ns == 0 && parent) || (ns && parent == 0)); + + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, 0, asFUNC_FUNCDEF); + if (func == 0) + return asOUT_OF_MEMORY; + + func->name = funcName; + func->nameSpace = ns; + func->module = this; + + asCFuncdefType *fdt = asNEW(asCFuncdefType)(engine, func); + funcDefs.PushLast(fdt); // The constructor set the refcount to 1 + + engine->funcDefs.PushLast(fdt); // doesn't increase refcount + func->id = engine->GetNextScriptFunctionId(); + engine->AddScriptFunction(func); + + if (parent) + { + parent->childFuncDefs.PushLast(fdt); + fdt->parentClass = parent; + } + + return (int)funcDefs.GetLength()-1; +} +#endif + +// interface +asDWORD asCModule::SetAccessMask(asDWORD mask) +{ + asDWORD old = accessMask; + accessMask = mask; + return old; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_objecttype.cpp b/3rdparty/angelscript/src/as_objecttype.cpp new file mode 100644 index 0000000..ef6000a --- /dev/null +++ b/3rdparty/angelscript/src/as_objecttype.cpp @@ -0,0 +1,686 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_objecttype.cpp +// +// A class for storing object type information +// + + +#include + +#include "as_config.h" +#include "as_objecttype.h" +#include "as_configgroup.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCObjectType::asCObjectType() : asCTypeInfo() +{ + derivedFrom = 0; + + acceptValueSubType = true; + acceptRefSubType = true; + +#ifdef WIP_16BYTE_ALIGN + alignment = 4; +#endif +} + +asCObjectType::asCObjectType(asCScriptEngine *in_engine) : asCTypeInfo(in_engine) +{ + derivedFrom = 0; + + acceptValueSubType = true; + acceptRefSubType = true; + +#ifdef WIP_16BYTE_ALIGN + alignment = 4; +#endif +} + +// interface +asUINT asCObjectType::GetChildFuncdefCount() const +{ + return childFuncDefs.GetLength(); +} + +// interface +asITypeInfo *asCObjectType::GetChildFuncdef(asUINT index) const +{ + if (index >= childFuncDefs.GetLength()) + return 0; + + return childFuncDefs[index]; +} + +// internal +void asCObjectType::DestroyInternal() +{ + if( engine == 0 ) return; + + // Skip this for list patterns as they do not increase the references + if( flags & asOBJ_LIST_PATTERN ) + { + // Clear the engine pointer to mark the object type as invalid + engine = 0; + return; + } + + // Release the object types held by the templateSubTypes + for( asUINT subtypeIndex = 0; subtypeIndex < templateSubTypes.GetLength(); subtypeIndex++ ) + { + if( templateSubTypes[subtypeIndex].GetTypeInfo() ) + templateSubTypes[subtypeIndex].GetTypeInfo()->ReleaseInternal(); + } + templateSubTypes.SetLength(0); + + // Clear the child types + for (asUINT n = 0; n < childFuncDefs.GetLength(); n++) + { + if( childFuncDefs[n] ) + childFuncDefs[n]->parentClass = 0; + } + childFuncDefs.SetLength(0); + + if( derivedFrom ) + derivedFrom->ReleaseInternal(); + derivedFrom = 0; + + ReleaseAllProperties(); + + ReleaseAllFunctions(); + + CleanUserData(); + + // Remove the type from the engine + if( typeId != -1 ) + engine->RemoveFromTypeIdMap(this); + + // Clear the engine pointer to mark the object type as invalid + engine = 0; +} + +asCObjectType::~asCObjectType() +{ + DestroyInternal(); +} + +// interface +bool asCObjectType::Implements(const asITypeInfo *objType) const +{ + if( this == objType ) + return true; + + for( asUINT n = 0; n < interfaces.GetLength(); n++ ) + if( interfaces[n] == objType ) return true; + + return false; +} + +// interface +bool asCObjectType::DerivesFrom(const asITypeInfo *objType) const +{ + if( this == objType ) + return true; + + asCObjectType *base = derivedFrom; + while( base ) + { + if( base == objType ) + return true; + + base = base->derivedFrom; + } + + return false; +} + +// interface +int asCObjectType::GetSubTypeId(asUINT subtypeIndex) const +{ + // This method is only supported for templates and template specializations + if( templateSubTypes.GetLength() == 0 ) + return asERROR; + + if( subtypeIndex >= templateSubTypes.GetLength() ) + return asINVALID_ARG; + + return engine->GetTypeIdFromDataType(templateSubTypes[subtypeIndex]); +} + +// interface +asITypeInfo *asCObjectType::GetSubType(asUINT subtypeIndex) const +{ + if( subtypeIndex >= templateSubTypes.GetLength() ) + return 0; + + return templateSubTypes[subtypeIndex].GetTypeInfo(); +} + +asUINT asCObjectType::GetSubTypeCount() const +{ + return asUINT(templateSubTypes.GetLength()); +} + +asUINT asCObjectType::GetInterfaceCount() const +{ + return asUINT(interfaces.GetLength()); +} + +asITypeInfo *asCObjectType::GetInterface(asUINT index) const +{ + return interfaces[index]; +} + +// internal +bool asCObjectType::IsInterface() const +{ + if( (flags & asOBJ_SCRIPT_OBJECT) && size == 0 ) + return true; + + return false; +} + +// interface +asUINT asCObjectType::GetFactoryCount() const +{ + return (asUINT)beh.factories.GetLength(); +} + +// interface +asIScriptFunction *asCObjectType::GetFactoryByIndex(asUINT index) const +{ + if( index >= beh.factories.GetLength() ) + return 0; + + return engine->GetFunctionById(beh.factories[index]); +} + +// interface +asIScriptFunction *asCObjectType::GetFactoryByDecl(const char *decl) const +{ + if( beh.factories.GetLength() == 0 ) + return 0; + + // Let the engine parse the string and find the appropriate factory function + return engine->GetFunctionById(engine->GetFactoryIdByDecl(this, decl)); +} + +// interface +asUINT asCObjectType::GetMethodCount() const +{ + return (asUINT)methods.GetLength(); +} + +// interface +asIScriptFunction *asCObjectType::GetMethodByIndex(asUINT index, bool getVirtual) const +{ + if( index >= methods.GetLength() ) + return 0; + + asCScriptFunction *func = engine->scriptFunctions[methods[index]]; + if( !getVirtual ) + { + if( func && func->funcType == asFUNC_VIRTUAL ) + return virtualFunctionTable[func->vfTableIdx]; + } + + return func; +} + +// interface +asIScriptFunction *asCObjectType::GetMethodByName(const char *in_name, bool in_getVirtual) const +{ + int id = -1; + for( asUINT n = 0; n < methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[methods[n]]->name == in_name ) + { + if( id == -1 ) + id = methods[n]; + else + return 0; + } + } + + if( id == -1 ) return 0; + + asCScriptFunction *func = engine->scriptFunctions[id]; + if( !in_getVirtual ) + { + if( func && func->funcType == asFUNC_VIRTUAL ) + return virtualFunctionTable[func->vfTableIdx]; + } + + return func; +} + +// interface +asIScriptFunction *asCObjectType::GetMethodByDecl(const char *decl, bool getVirtual) const +{ + if( methods.GetLength() == 0 ) + return 0; + + // Get the module from one of the methods, but it will only be + // used to allow the parsing of types not already known by the object. + // It is possible for object types to be orphaned, e.g. by discarding + // the module that created it. In this case it is still possible to + // find the methods, but any type not known by the object will result in + // an invalid declaration. + asCModule *mod = engine->scriptFunctions[methods[0]]->module; + int id = engine->GetMethodIdByDecl(this, decl, mod); + if( id <= 0 ) + return 0; + + if( !getVirtual ) + { + asCScriptFunction *func = engine->scriptFunctions[id]; + if( func && func->funcType == asFUNC_VIRTUAL ) + return virtualFunctionTable[func->vfTableIdx]; + } + + return engine->scriptFunctions[id]; +} + +// interface +asUINT asCObjectType::GetPropertyCount() const +{ + return (asUINT)properties.GetLength(); +} + +// interface +int asCObjectType::GetProperty(asUINT index, const char **out_name, int *out_typeId, bool *out_isPrivate, bool *out_isProtected, int *out_offset, bool *out_isReference, asDWORD *out_accessMask) const +{ + if( index >= properties.GetLength() ) + return asINVALID_ARG; + + asCObjectProperty *prop = properties[index]; + if( out_name ) + *out_name = prop->name.AddressOf(); + if( out_typeId ) + *out_typeId = engine->GetTypeIdFromDataType(prop->type); + if( out_isPrivate ) + *out_isPrivate = prop->isPrivate; + if( out_isProtected ) + *out_isProtected = prop->isProtected; + if( out_offset ) + *out_offset = prop->byteOffset; + if( out_isReference ) + *out_isReference = prop->type.IsReference(); + if( out_accessMask ) + *out_accessMask = prop->accessMask; + + return 0; +} + +// interface +const char *asCObjectType::GetPropertyDeclaration(asUINT index, bool includeNamespace) const +{ + if( index >= properties.GetLength() ) + return 0; + + asCString *tempString = &asCThreadManager::GetLocalData()->string; + if( properties[index]->isPrivate ) + *tempString = "private "; + else if( properties[index]->isProtected ) + *tempString = "protected "; + else + *tempString = ""; + *tempString += properties[index]->type.Format(nameSpace, includeNamespace); + *tempString += " "; + *tempString += properties[index]->name; + + return tempString->AddressOf(); +} + +asITypeInfo *asCObjectType::GetBaseType() const +{ + return derivedFrom; +} + +asUINT asCObjectType::GetBehaviourCount() const +{ + // Count the number of behaviours (except factory functions) + asUINT count = 0; + + if( beh.destruct ) count++; + if( beh.addref ) count++; + if( beh.release ) count++; + if( beh.gcGetRefCount ) count++; + if( beh.gcSetFlag ) count++; + if( beh.gcGetFlag ) count++; + if( beh.gcEnumReferences ) count++; + if( beh.gcReleaseAllReferences ) count++; + if( beh.templateCallback ) count++; + if( beh.listFactory ) count++; + if( beh.getWeakRefFlag ) count++; + + // For reference types, the factories are also stored in the constructor + // list, so it is sufficient to enumerate only those + count += (asUINT)beh.constructors.GetLength(); + + return count; +} + +asIScriptFunction *asCObjectType::GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const +{ + // Find the correct behaviour + asUINT count = 0; + + if( beh.destruct && count++ == index ) // only increase count if the behaviour is registered + { + if( outBehaviour ) *outBehaviour = asBEHAVE_DESTRUCT; + return engine->scriptFunctions[beh.destruct]; + } + + if( beh.addref && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_ADDREF; + return engine->scriptFunctions[beh.addref]; + } + + if( beh.release && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_RELEASE; + return engine->scriptFunctions[beh.release]; + } + + if( beh.gcGetRefCount && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_GETREFCOUNT; + return engine->scriptFunctions[beh.gcGetRefCount]; + } + + if( beh.gcSetFlag && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_SETGCFLAG; + return engine->scriptFunctions[beh.gcSetFlag]; + } + + if( beh.gcGetFlag && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_GETGCFLAG; + return engine->scriptFunctions[beh.gcGetFlag]; + } + + if( beh.gcEnumReferences && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_ENUMREFS; + return engine->scriptFunctions[beh.gcEnumReferences]; + } + + if( beh.gcReleaseAllReferences && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_RELEASEREFS; + return engine->scriptFunctions[beh.gcReleaseAllReferences]; + } + + if( beh.templateCallback && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_TEMPLATE_CALLBACK; + return engine->scriptFunctions[beh.templateCallback]; + } + + if( beh.listFactory && count++ == index ) + { + if( outBehaviour ) + { + if( flags & asOBJ_VALUE ) + *outBehaviour = asBEHAVE_LIST_CONSTRUCT; + else + *outBehaviour = asBEHAVE_LIST_FACTORY; + } + + return engine->scriptFunctions[beh.listFactory]; + } + + if( beh.getWeakRefFlag && count++ == index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_GET_WEAKREF_FLAG; + return engine->scriptFunctions[beh.getWeakRefFlag]; + } + + // For reference types, the factories are also stored in the constructor + // list, so it is sufficient to enumerate only those + if( index - count < beh.constructors.GetLength() ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_CONSTRUCT; + return engine->scriptFunctions[beh.constructors[index - count]]; + } + else + count += (asUINT)beh.constructors.GetLength(); + + return 0; +} + +// internal +asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &propName, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited) +{ + asASSERT( flags & asOBJ_SCRIPT_OBJECT ); + asASSERT( dt.CanBeInstantiated() ); + asASSERT( !IsInterface() ); + + // Store the properties in the object type descriptor + asCObjectProperty *prop = asNEW(asCObjectProperty); + if( prop == 0 ) + { + // Out of memory + return 0; + } + + prop->name = propName; + prop->type = dt; + prop->isPrivate = isPrivate; + prop->isProtected = isProtected; + prop->isInherited = isInherited; + + int propSize; + if( dt.IsObject() ) + { + // Non-POD value types can't be allocated inline, + // because there is a risk that the script might + // try to access the content without knowing that + // it hasn't been initialized yet. + if( dt.GetTypeInfo()->flags & asOBJ_POD ) + propSize = dt.GetSizeInMemoryBytes(); + else + { + propSize = dt.GetSizeOnStackDWords()*4; + if( !dt.IsObjectHandle() ) + prop->type.MakeReference(true); + } + } + else if (dt.IsFuncdef()) + { + // Funcdefs don't have a size, as they must always be stored as handles + asASSERT(dt.IsObjectHandle()); + propSize = AS_PTR_SIZE * 4; + } + else + propSize = dt.GetSizeInMemoryBytes(); + + // Add extra bytes so that the property will be properly aligned +#ifndef WIP_16BYTE_ALIGN + if( propSize == 2 && (size & 1) ) size += 1; + if( propSize > 2 && (size & 3) ) size += 4 - (size & 3); +#else + asUINT alignment = dt.GetAlignment(); + const asUINT propSizeAlignmentDifference = size & (alignment-1); + if( propSizeAlignmentDifference != 0 ) + { + size += (alignment - propSizeAlignmentDifference); + } + + asASSERT((size % alignment) == 0); +#endif + + prop->byteOffset = size; + size += propSize; + + properties.PushLast(prop); + + // Make sure the struct holds a reference to the config group where the object is registered + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(prop->type.GetTypeInfo()); + if( group != 0 ) group->AddRef(); + + // Add reference to object types + asCTypeInfo *type = prop->type.GetTypeInfo(); + if( type ) + type->AddRefInternal(); + + return prop; +} + +// internal +void asCObjectType::ReleaseAllProperties() +{ + for( asUINT n = 0; n < properties.GetLength(); n++ ) + { + if( properties[n] ) + { + if( flags & asOBJ_SCRIPT_OBJECT ) + { + // Release the config group for script classes that are being destroyed + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(properties[n]->type.GetTypeInfo()); + if( group != 0 ) group->Release(); + + // Release references to objects types + asCTypeInfo *type = properties[n]->type.GetTypeInfo(); + if( type ) + type->ReleaseInternal(); + } + else + { + // Release template instance types (ref increased by RegisterObjectProperty) + asCTypeInfo *type = properties[n]->type.GetTypeInfo(); + if( type ) + type->ReleaseInternal(); + } + + asDELETE(properties[n],asCObjectProperty); + } + } + + properties.SetLength(0); +} + +// internal +void asCObjectType::ReleaseAllFunctions() +{ + beh.factory = 0; + beh.copyfactory = 0; + for( asUINT a = 0; a < beh.factories.GetLength(); a++ ) + { + if( engine->scriptFunctions[beh.factories[a]] ) + engine->scriptFunctions[beh.factories[a]]->ReleaseInternal(); + } + beh.factories.SetLength(0); + + beh.construct = 0; + beh.copyconstruct = 0; + for( asUINT b = 0; b < beh.constructors.GetLength(); b++ ) + { + if( engine->scriptFunctions[beh.constructors[b]] ) + engine->scriptFunctions[beh.constructors[b]]->ReleaseInternal(); + } + beh.constructors.SetLength(0); + + if( beh.templateCallback ) + engine->scriptFunctions[beh.templateCallback]->ReleaseInternal(); + beh.templateCallback = 0; + + if( beh.listFactory ) + engine->scriptFunctions[beh.listFactory]->ReleaseInternal(); + beh.listFactory = 0; + + if( beh.destruct ) + engine->scriptFunctions[beh.destruct]->ReleaseInternal(); + beh.destruct = 0; + + if( beh.copy ) + engine->scriptFunctions[beh.copy]->ReleaseInternal(); + beh.copy = 0; + + for( asUINT c = 0; c < methods.GetLength(); c++ ) + { + if( engine->scriptFunctions[methods[c]] ) + engine->scriptFunctions[methods[c]]->ReleaseInternal(); + } + methods.SetLength(0); + + for( asUINT d = 0; d < virtualFunctionTable.GetLength(); d++ ) + { + if( virtualFunctionTable[d] ) + virtualFunctionTable[d]->ReleaseInternal(); + } + virtualFunctionTable.SetLength(0); + + // GC behaviours + if( beh.addref ) + engine->scriptFunctions[beh.addref]->ReleaseInternal(); + beh.addref = 0; + + if( beh.release ) + engine->scriptFunctions[beh.release]->ReleaseInternal(); + beh.release = 0; + + if( beh.gcEnumReferences ) + engine->scriptFunctions[beh.gcEnumReferences]->ReleaseInternal(); + beh.gcEnumReferences = 0; + + if( beh.gcGetFlag ) + engine->scriptFunctions[beh.gcGetFlag]->ReleaseInternal(); + beh.gcGetFlag = 0; + + if( beh.gcGetRefCount ) + engine->scriptFunctions[beh.gcGetRefCount]->ReleaseInternal(); + beh.gcGetRefCount = 0; + + if( beh.gcReleaseAllReferences ) + engine->scriptFunctions[beh.gcReleaseAllReferences]->ReleaseInternal(); + beh.gcReleaseAllReferences = 0; + + if( beh.gcSetFlag ) + engine->scriptFunctions[beh.gcSetFlag]->ReleaseInternal(); + beh.gcSetFlag = 0; + + if ( beh.getWeakRefFlag ) + engine->scriptFunctions[beh.getWeakRefFlag]->ReleaseInternal(); + beh.getWeakRefFlag = 0; +} + +END_AS_NAMESPACE + + + diff --git a/3rdparty/angelscript/src/as_outputbuffer.cpp b/3rdparty/angelscript/src/as_outputbuffer.cpp new file mode 100644 index 0000000..433f820 --- /dev/null +++ b/3rdparty/angelscript/src/as_outputbuffer.cpp @@ -0,0 +1,109 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2012 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_outputbuffer.cpp +// +// This class appends strings to one large buffer that can later +// be sent to the real output stream +// + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_outputbuffer.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCOutputBuffer::~asCOutputBuffer() +{ + Clear(); +} + +void asCOutputBuffer::Clear() +{ + for( asUINT n = 0; n < messages.GetLength(); n++ ) + { + if( messages[n] ) + { + asDELETE(messages[n],message_t); + } + } + messages.SetLength(0); +} + +void asCOutputBuffer::Callback(asSMessageInfo *msg) +{ + message_t *msgInfo = asNEW(message_t); + if( msgInfo == 0 ) + return; + + msgInfo->section = msg->section; + msgInfo->row = msg->row; + msgInfo->col = msg->col; + msgInfo->type = msg->type; + msgInfo->msg = msg->message; + + messages.PushLast(msgInfo); +} + +void asCOutputBuffer::Append(asCOutputBuffer &in) +{ + for( asUINT n = 0; n < in.messages.GetLength(); n++ ) + messages.PushLast(in.messages[n]); + in.messages.SetLength(0); +} + +void asCOutputBuffer::SendToCallback(asCScriptEngine *engine, asSSystemFunctionInterface *func, void *obj) +{ + for( asUINT n = 0; n < messages.GetLength(); n++ ) + { + asSMessageInfo msg; + msg.section = messages[n]->section.AddressOf(); + msg.row = messages[n]->row; + msg.col = messages[n]->col; + msg.type = messages[n]->type; + msg.message = messages[n]->msg.AddressOf(); + + if( func->callConv < ICC_THISCALL ) + engine->CallGlobalFunction(&msg, obj, func, 0); + else + engine->CallObjectMethod(obj, &msg, func, 0); + } + Clear(); +} + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + diff --git a/3rdparty/angelscript/src/as_parser.cpp b/3rdparty/angelscript/src/as_parser.cpp new file mode 100644 index 0000000..df2e702 --- /dev/null +++ b/3rdparty/angelscript/src/as_parser.cpp @@ -0,0 +1,4429 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_parser.cpp +// +// This class parses the script code and builds a tree for compilation +// +// +// I've documented the syntax in Extended BNF. You'll find it by doing a search in +// this file by "BNF:". The starting point for the script language is SCRIPT ::=. +// +// Ref: http://matt.might.net/articles/grammars-bnf-ebnf/ +// +// ( ) - used for grouping +// { } - 0 or more repetitions +// [ ] - optional +// | - or +// ' ' - token +// + + + +#include "as_config.h" +#include "as_parser.h" +#include "as_tokendef.h" +#include "as_texts.h" +#include "as_debug.h" + +#ifdef _MSC_VER +#pragma warning(disable:4702) // unreachable code +#endif + +BEGIN_AS_NAMESPACE + +asCParser::asCParser(asCBuilder *builder) +{ + this->builder = builder; + this->engine = builder->engine; + + script = 0; + scriptNode = 0; + checkValidTypes = false; + isParsingAppInterface = false; +} + +asCParser::~asCParser() +{ + Reset(); +} + +void asCParser::Reset() +{ + errorWhileParsing = false; + isSyntaxError = false; + checkValidTypes = false; + isParsingAppInterface = false; + + sourcePos = 0; + + if( scriptNode ) + { + scriptNode->Destroy(engine); + } + + scriptNode = 0; + + script = 0; + + lastToken.pos = size_t(-1); +} + +asCScriptNode *asCParser::GetScriptNode() +{ + return scriptNode; +} + +int asCParser::ParseFunctionDefinition(asCScriptCode *in_script, bool in_expectListPattern) +{ + Reset(); + + // Set flag that permits ? as datatype for parameters + isParsingAppInterface = true; + + this->script = in_script; + + scriptNode = ParseFunctionDefinition(); + + if( in_expectListPattern ) + scriptNode->AddChildLast(ParseListPattern()); + + // The declaration should end after the definition + if( !isSyntaxError ) + { + sToken t; + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); + return -1; + } + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + +asCScriptNode *asCParser::CreateNode(eScriptNode type) +{ + void *ptr = engine->memoryMgr.AllocScriptNode(); + if( ptr == 0 ) + { + // Out of memory + errorWhileParsing = true; + return 0; + } + + return new(ptr) asCScriptNode(type); +} + +int asCParser::ParseDataType(asCScriptCode *in_script, bool in_isReturnType) +{ + Reset(); + + this->script = in_script; + + scriptNode = CreateNode(snDataType); + if( scriptNode == 0 ) return -1; + + scriptNode->AddChildLast(ParseType(true)); + if( isSyntaxError ) return -1; + + if( in_isReturnType ) + { + scriptNode->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return -1; + } + + // The declaration should end after the type + sToken t; + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); + return -1; + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + + +// Parse a template declaration: IDENTIFIER '<' 'class'? IDENTIFIER '>' +int asCParser::ParseTemplateDecl(asCScriptCode *in_script) +{ + Reset(); + + this->script = in_script; + scriptNode = CreateNode(snUndefined); + if( scriptNode == 0 ) return -1; + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + sToken t; + GetToken(&t); + if( t.type != ttLessThan ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t); + Error(InsteadFound(t), &t); + return -1; + } + + // The class token is optional + GetToken(&t); + if( t.type != ttClass ) + RewindTo(&t); + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + // There can be multiple sub types + GetToken(&t); + + // Parse template types by list separator + while(t.type == ttListSeparator) + { + GetToken(&t); + if( t.type != ttClass ) + RewindTo(&t); + scriptNode->AddChildLast(ParseIdentifier()); + + if( isSyntaxError ) return -1; + GetToken(&t); + } + + if( t.type != ttGreaterThan ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t); + Error(InsteadFound(t), &t); + return -1; + } + + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); + return -1; + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + +int asCParser::ParsePropertyDeclaration(asCScriptCode *in_script) +{ + Reset(); + + this->script = in_script; + + scriptNode = CreateNode(snDeclaration); + if( scriptNode == 0 ) return -1; + + scriptNode->AddChildLast(ParseType(true)); + if( isSyntaxError ) return -1; + + // Allow optional '&' to indicate that the property is indirect, i.e. stored as reference + sToken t; + GetToken(&t); + RewindTo(&t); + if( t.type == ttAmp ) + scriptNode->AddChildLast(ParseToken(ttAmp)); + + // Allow optional namespace to be defined before the identifier in case + // the declaration is to be used for searching for an existing property + ParseOptionalScope(scriptNode); + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + // The declaration should end after the identifier + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); + return -1; + } + + return 0; +} + +// BNF: SCOPE ::= ['::'] {IDENTIFIER '::'} [IDENTIFIER ['<' TYPE {',' TYPE} '>'] '::'] +void asCParser::ParseOptionalScope(asCScriptNode *node) +{ + asCScriptNode *scope = CreateNode(snScope); + + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + if( t1.type == ttScope ) + { + RewindTo(&t1); + scope->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + GetToken(&t2); + } + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + RewindTo(&t1); + scope->AddChildLast(ParseIdentifier()); + scope->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + GetToken(&t2); + } + + // The innermost scope may be a template type + if( t1.type == ttIdentifier && t2.type == ttLessThan ) + { + tempString.Assign(&script->code[t1.pos], t1.length); + if (engine->IsTemplateType(tempString.AddressOf())) + { + RewindTo(&t1); + asCScriptNode *restore = scope->lastChild; + scope->AddChildLast(ParseIdentifier()); + if (ParseTemplTypeList(scope, false)) + { + GetToken(&t2); + if (t2.type == ttScope) + { + // Template type is part of the scope + // Nothing more needs to be done + node->AddChildLast(scope); + return; + } + else + { + // The template type is not part of the scope + // Rewind to the template type and end the scope + RewindTo(&t1); + + // Restore the previously parsed node + while (scope->lastChild != restore) + { + asCScriptNode *last = scope->lastChild; + last->DisconnectParent(); + last->Destroy(engine); + } + if( scope->lastChild ) + node->AddChildLast(scope); + else + scope->Destroy(engine); + return; + } + } + } + } + + // The identifier is not part of the scope + RewindTo(&t1); + + if (scope->lastChild) + node->AddChildLast(scope); + else + scope->Destroy(engine); +} + +asCScriptNode *asCParser::ParseFunctionDefinition() +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + ParseOptionalScope(node); + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + // Parse an optional const after the function definition (used for object methods) + sToken t1; + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + + return node; +} + +// BNF: TYPEMOD ::= ['&' ['in' | 'out' | 'inout']] +asCScriptNode *asCParser::ParseTypeMod(bool isParam) +{ + asCScriptNode *node = CreateNode(snDataType); + if( node == 0 ) return 0; + + sToken t; + + // Parse possible & token + GetToken(&t); + RewindTo(&t); + if( t.type == ttAmp ) + { + node->AddChildLast(ParseToken(ttAmp)); + if( isSyntaxError ) return node; + + if( isParam ) + { + GetToken(&t); + RewindTo(&t); + + if( t.type == ttIn || t.type == ttOut || t.type == ttInOut ) + { + int tokens[3] = {ttIn, ttOut, ttInOut}; + node->AddChildLast(ParseOneOf(tokens, 3)); + } + } + } + + // Parse possible + token + GetToken(&t); + RewindTo(&t); + if( t.type == ttPlus ) + { + node->AddChildLast(ParseToken(ttPlus)); + if( isSyntaxError ) return node; + } + + // Parse possible if_handle_then_const token + GetToken(&t); + RewindTo(&t); + if (IdentifierIs(t, IF_HANDLE_TOKEN)) + { + node->AddChildLast(ParseToken(ttIdentifier)); + if (isSyntaxError) return node; + } + + return node; +} + +// BNF: TYPE ::= ['const'] SCOPE DATATYPE ['<' TYPE {',' TYPE} '>'] { ('[' ']') | '@' } +asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, bool allowAuto) +{ + asCScriptNode *node = CreateNode(snDataType); + if( node == 0 ) return 0; + + sToken t; + + if( allowConst ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttConst ) + { + node->AddChildLast(ParseToken(ttConst)); + if( isSyntaxError ) return node; + } + } + + // Parse scope prefix + ParseOptionalScope(node); + + // Parse the actual type + node->AddChildLast(ParseDataType(allowVariableType, allowAuto)); + if( isSyntaxError ) return node; + + // If the datatype is a template type, then parse the subtype within the < > + GetToken(&t); + RewindTo(&t); + asCScriptNode *type = node->lastChild; + tempString.Assign(&script->code[type->tokenPos], type->tokenLength); + if( engine->IsTemplateType(tempString.AddressOf()) && t.type == ttLessThan ) + { + ParseTemplTypeList(node); + if (isSyntaxError) return node; + } + + // Parse [] and @ + GetToken(&t); + RewindTo(&t); + while( t.type == ttOpenBracket || t.type == ttHandle) + { + if( t.type == ttOpenBracket ) + { + node->AddChildLast(ParseToken(ttOpenBracket)); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseBracket ) + { + Error(ExpectedToken("]"), &t); + Error(InsteadFound(t), &t); + return node; + } + } + else + { + node->AddChildLast(ParseToken(ttHandle)); + if( isSyntaxError ) return node; + } + + GetToken(&t); + RewindTo(&t); + } + + return node; +} + +// This parses a template type list, e.g. +// If 'required' is false, and the template type list is not valid, +// then no change will be done and the function returns false. This +// can be used as do an optional parsing +bool asCParser::ParseTemplTypeList(asCScriptNode *node, bool required) +{ + sToken t; + bool isValid = true; + + // Remember the last child, so we can restore the state if needed + asCScriptNode *last = node->lastChild; + + // Starts with '<' + GetToken(&t); + if (t.type != ttLessThan) + { + if (required) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t); + Error(InsteadFound(t), &t); + } + return false; + } + + // At least one type + // TODO: child funcdef: Make this work with !required + node->AddChildLast(ParseType(true, false)); + if (isSyntaxError) return false; + + GetToken(&t); + + // Parse template types by list separator + while (t.type == ttListSeparator) + { + // TODO: child funcdef: Make this work with !required + node->AddChildLast(ParseType(true, false)); + if (isSyntaxError) return false; + GetToken(&t); + } + + // End with '>' + // Accept >> and >>> tokens too. But then force the tokenizer to move + // only 1 character ahead (thus splitting the token in two). + if (script->code[t.pos] != '>') + { + if (required) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t); + Error(InsteadFound(t), &t); + } + else + isValid = false; + } + else + { + // Break the token so that only the first > is parsed + SetPos(t.pos + 1); + } + + if (!required && !isValid) + { + // Restore the original state before returning + while (node->lastChild != last) + { + asCScriptNode *n = node->lastChild; + n->DisconnectParent(); + n->Destroy(engine); + } + + return false; + } + + // The template type list was parsed OK + return true; +} + +asCScriptNode *asCParser::ParseToken(int token) +{ + asCScriptNode *node = CreateNode(snUndefined); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type != token ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(token)), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseOneOf(int *tokens, int count) +{ + asCScriptNode *node = CreateNode(snUndefined); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + int n; + for( n = 0; n < count; n++ ) + { + if( tokens[n] == t1.type ) + break; + } + if( n == count ) + { + Error(ExpectedOneOf(tokens, count), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: DATATYPE ::= (IDENTIFIER | PRIMTYPE | '?' | 'auto') +asCScriptNode *asCParser::ParseDataType(bool allowVariableType, bool allowAuto) +{ + asCScriptNode *node = CreateNode(snDataType); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( !IsDataType(t1) && !(allowVariableType && t1.type == ttQuestion) && !(allowAuto && t1.type == ttAuto) ) + { + if( t1.type == ttIdentifier ) + { + asCString errMsg; + tempString.Assign(&script->code[t1.pos], t1.length); + errMsg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, tempString.AddressOf()); + Error(errMsg, &t1); + } + else if( t1.type == ttAuto ) + { + Error(TXT_AUTO_NOT_ALLOWED, &t1); + } + else + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: PRIMTYPE ::= 'void' | 'int' | 'int8' | 'int16' | 'int32' | 'int64' | 'uint' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'float' | 'double' | 'bool' +asCScriptNode *asCParser::ParseRealType() +{ + asCScriptNode *node = CreateNode(snDataType); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( !IsRealType(t1.type) ) + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: IDENTIFIER ::= single token: starts with letter or _, can include any letter and digit, same as in C++ +asCScriptNode *asCParser::ParseIdentifier() +{ + asCScriptNode *node = CreateNode(snIdentifier); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type != ttIdentifier ) + { + Error(TXT_EXPECTED_IDENTIFIER, &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: PARAMLIST ::= '(' ['void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]})] ')' +asCScriptNode *asCParser::ParseParameterList() +{ + asCScriptNode *node = CreateNode(snParameterList); + if( node == 0 ) return 0; + + sToken t1; + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + // If the parameter list is just (void) then the void token should be ignored + if( t1.type == ttVoid ) + { + sToken t2; + GetToken(&t2); + if( t2.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t2.pos, t2.length); + return node; + } + } + + RewindTo(&t1); + + for(;;) + { + // Parse data type + node->AddChildLast(ParseType(true, isParsingAppInterface)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(true)); + if( isSyntaxError ) return node; + + // Parse optional identifier + GetToken(&t1); + if( t1.type == ttIdentifier ) + { + RewindTo(&t1); + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t1); + } + + // Parse optional expression for the default arg + if( t1.type == ttAssignment ) + { + // Do a superficial parsing of the default argument + // The actual parsing will be done when the argument is compiled for a function call + node->AddChildLast(SuperficiallyParseExpression()); + if( isSyntaxError ) return node; + + GetToken(&t1); + } + + // Check if list continues + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + return node; + } + else if( t1.type == ttListSeparator ) + continue; + else + { + Error(ExpectedTokens(")", ","), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::SuperficiallyParseExpression() +{ + asCScriptNode *node = CreateNode(snExpression); + if( node == 0 ) return 0; + + // Simply parse everything until the first , or ), whichever comes first. + // Keeping in mind that () and {} can group expressions. + + sToken start; + GetToken(&start); + RewindTo(&start); + + asCString stack; + sToken t; + for(;;) + { + GetToken(&t); + + if( t.type == ttOpenParanthesis ) + stack += "("; + else if( t.type == ttCloseParanthesis ) + { + if( stack == "" ) + { + // Expression has ended. This token is not part of expression + RewindTo(&t); + break; + } + else if( stack[stack.GetLength()-1] == '(' ) + { + // Group has ended + stack.SetLength(stack.GetLength()-1); + } + else + { + // Wrong syntax + RewindTo(&t); + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, ")"); + Error(str, &t); + return node; + } + } + else if( t.type == ttListSeparator ) + { + if( stack == "" ) + { + // Expression has ended. This token is not part of expression + RewindTo(&t); + break; + } + } + else if( t.type == ttStartStatementBlock ) + stack += "{"; + else if( t.type == ttEndStatementBlock ) + { + if( stack == "" || stack[stack.GetLength()-1] != '{' ) + { + // Wrong syntax + RewindTo(&t); + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, "}"); + Error(str, &t); + return node; + } + else + { + // Group has ended + stack.SetLength(stack.GetLength()-1); + } + } + else if( t.type == ttEndStatement ) + { + // Wrong syntax (since we're parsing a default arg expression) + RewindTo(&t); + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, ";"); + Error(str, &t); + return node; + } + else if( t.type == ttNonTerminatedStringConstant ) + { + RewindTo(&t); + Error(TXT_NONTERMINATED_STRING, &t); + return node; + } + else if( t.type == ttEnd ) + { + // Wrong syntax + RewindTo(&t); + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + Info(TXT_WHILE_PARSING_EXPRESSION, &start); + return node; + } + + // Include the token in the node + node->UpdateSourcePos(t.pos, t.length); + } + + return node; +} + +void asCParser::GetToken(sToken *token) +{ + // Check if the token has already been parsed + if( lastToken.pos == sourcePos ) + { + *token = lastToken; + sourcePos += token->length; + + if( token->type == ttWhiteSpace || + token->type == ttOnelineComment || + token->type == ttMultilineComment ) + GetToken(token); + + return; + } + + // Parse new token + size_t sourceLength = script->codeLength; + do + { + if( sourcePos >= sourceLength ) + { + token->type = ttEnd; + token->length = 0; + } + else + token->type = engine->tok.GetToken(&script->code[sourcePos], sourceLength - sourcePos, &token->length); + + token->pos = sourcePos; + + // Update state + sourcePos += token->length; + } + // Filter out whitespace and comments + while( token->type == ttWhiteSpace || + token->type == ttOnelineComment || + token->type == ttMultilineComment ); +} + +void asCParser::SetPos(size_t pos) +{ + lastToken.pos = size_t(-1); + sourcePos = pos; +} + +void asCParser::RewindTo(const sToken *token) +{ + // TODO: optimize: Perhaps we can optimize this further by having the parser + // set an explicit return point, after which each token will + // be stored. That way not just one token will be reused but + // no token will have to be tokenized more than once. + + // Store the token so it doesn't have to be tokenized again + lastToken = *token; + + sourcePos = token->pos; +} + +void asCParser::Error(const asCString &text, sToken *token) +{ + RewindTo(token); + + isSyntaxError = true; + errorWhileParsing = true; + + int row, col; + script->ConvertPosToRowCol(token->pos, &row, &col); + + if( builder ) + builder->WriteError(script->name, text, row, col); +} + +void asCParser::Warning(const asCString &text, sToken *token) +{ + int row, col; + script->ConvertPosToRowCol(token->pos, &row, &col); + + if( builder ) + builder->WriteWarning(script->name, text, row, col); +} + +void asCParser::Info(const asCString &text, sToken *token) +{ + RewindTo(token); + + isSyntaxError = true; + errorWhileParsing = true; + + int row, col; + script->ConvertPosToRowCol(token->pos, &row, &col); + + if( builder ) + builder->WriteInfo(script->name, text, row, col, false); +} + +bool asCParser::IsRealType(int tokenType) +{ + if( tokenType == ttVoid || + tokenType == ttInt || + tokenType == ttInt8 || + tokenType == ttInt16 || + tokenType == ttInt64 || + tokenType == ttUInt || + tokenType == ttUInt8 || + tokenType == ttUInt16 || + tokenType == ttUInt64 || + tokenType == ttFloat || + tokenType == ttBool || + tokenType == ttDouble ) + return true; + + return false; +} + +bool asCParser::IsDataType(const sToken &token) +{ + if( token.type == ttIdentifier ) + { +#ifndef AS_NO_COMPILER + if( checkValidTypes ) + { + // Check if this is an existing type, regardless of namespace + tempString.Assign(&script->code[token.pos], token.length); + if( !builder->DoesTypeExist(tempString.AddressOf()) ) + return false; + } +#endif + return true; + } + + if( IsRealType(token.type) ) + return true; + + return false; +} + +asCString asCParser::ExpectedToken(const char *token) +{ + asCString str; + + str.Format(TXT_EXPECTED_s, token); + + return str; +} + +asCString asCParser::ExpectedTokens(const char *t1, const char *t2) +{ + asCString str; + + str.Format(TXT_EXPECTED_s_OR_s, t1, t2); + + return str; +} + +asCString asCParser::ExpectedOneOf(int *tokens, int count) +{ + asCString str; + + str = TXT_EXPECTED_ONE_OF; + for( int n = 0; n < count; n++ ) + { + str += asCTokenizer::GetDefinition(tokens[n]); + if( n < count-1 ) + str += ", "; + } + + return str; +} + +asCString asCParser::ExpectedOneOf(const char **tokens, int count) +{ + asCString str; + + str = TXT_EXPECTED_ONE_OF; + for( int n = 0; n < count; n++ ) + { + str += tokens[n]; + if( n < count-1 ) + str += ", "; + } + + return str; +} + +asCString asCParser::InsteadFound(sToken &t) +{ + asCString str; + if( t.type == ttIdentifier ) + { + asCString id(&script->code[t.pos], t.length); + str.Format(TXT_INSTEAD_FOUND_IDENTIFIER_s, id.AddressOf()); + } + else if( t.type >= ttIf ) + str.Format(TXT_INSTEAD_FOUND_KEYWORD_s, asCTokenizer::GetDefinition(t.type)); + else + str.Format(TXT_INSTEAD_FOUND_s, asCTokenizer::GetDefinition(t.type)); + + return str; +} + +asCScriptNode *asCParser::ParseListPattern() +{ + asCScriptNode *node = CreateNode(snListPattern); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + sToken start = t1; + + bool isBeginning = true; + bool afterType = false; + while( !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + if( !afterType ) + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } + break; + } + else if( t1.type == ttStartStatementBlock ) + { + if( afterType ) + { + Error(ExpectedTokens(",","}"), &t1); + Error(InsteadFound(t1), &t1); + } + RewindTo(&t1); + node->AddChildLast(ParseListPattern()); + afterType = true; + } + else if( t1.type == ttIdentifier && (IdentifierIs(t1, "repeat") || IdentifierIs(t1, "repeat_same")) ) + { + if( !isBeginning ) + { + asCString msg; + asCString token(&script->code[t1.pos], t1.length); + msg.Format(TXT_UNEXPECTED_TOKEN_s, token.AddressOf()); + Error(msg.AddressOf(), &t1); + } + RewindTo(&t1); + node->AddChildLast(ParseIdentifier()); + } + else if( t1.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start); + break; + } + else if( t1.type == ttListSeparator ) + { + if( !afterType ) + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } + afterType = false; + } + else + { + if( afterType ) + { + Error(ExpectedTokens(",", "}"), &t1); + Error(InsteadFound(t1), &t1); + } + RewindTo(&t1); + node->AddChildLast(ParseType(true, true)); + afterType = true; + } + + isBeginning = false; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +bool asCParser::IdentifierIs(const sToken &t, const char *str) +{ + if( t.type != ttIdentifier ) + return false; + + return script->TokenEquals(t.pos, t.length, str); +} + +#ifndef AS_NO_COMPILER + +// This function will return true if the current token is not a template, or if it is and +// the following has a valid syntax for a template type. The source position will be left +// at the first token after the type in case of success +bool asCParser::CheckTemplateType(const sToken &t) +{ + // Is this a template type? + tempString.Assign(&script->code[t.pos], t.length); + if( engine->IsTemplateType(tempString.AddressOf()) ) + { + // If the next token is a < then parse the sub-type too + sToken t1; + GetToken(&t1); + if( t1.type != ttLessThan ) + { + RewindTo(&t1); + return true; + } + + for(;;) + { + // There might optionally be a 'const' + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + // The type may be initiated with the scope operator + if( t1.type == ttScope ) + GetToken(&t1); + + // There may be multiple levels of scope operators + sToken t2; + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + RewindTo(&t2); + + // Now there must be a data type + if( !IsDataType(t1) ) + return false; + + if( !CheckTemplateType(t1) ) + return false; + + GetToken(&t1); + + // Is it a handle or array? + while( t1.type == ttHandle || t1.type == ttOpenBracket ) + { + if( t1.type == ttOpenBracket ) + { + GetToken(&t1); + if( t1.type != ttCloseBracket ) + return false; + } + + GetToken(&t1); + } + + // Was this the last template subtype? + if( t1.type != ttListSeparator ) + break; + } + + // Accept >> and >>> tokens too. But then force the tokenizer to move + // only 1 character ahead (thus splitting the token in two). + if( script->code[t1.pos] != '>' ) + return false; + else if( t1.length != 1 ) + { + // We need to break the token, so that only the first character is parsed + SetPos(t1.pos + 1); + } + } + + return true; +} + +// BNF: CAST ::= 'cast' '<' TYPE '>' '(' ASSIGN ')' +asCScriptNode *asCParser::ParseCast() +{ + asCScriptNode *node = CreateNode(snCast); + if( node == 0 ) return 0; + + sToken t1; + GetToken(&t1); + if( t1.type != ttCast ) + { + Error(ExpectedToken("cast"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type != ttLessThan ) + { + Error(ExpectedToken("<"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + // Parse the data type + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttGreaterThan ) + { + Error(ExpectedToken(">"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' | LAMBDA +asCScriptNode *asCParser::ParseExprValue() +{ + asCScriptNode *node = CreateNode(snExprValue); + if( node == 0 ) return 0; + + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + // 'void' is a special expression that doesn't do anything (normally used for skipping output arguments) + if( t1.type == ttVoid ) + node->AddChildLast(ParseToken(ttVoid)); + else if( IsRealType(t1.type) ) + node->AddChildLast(ParseConstructCall()); + else if( t1.type == ttIdentifier || t1.type == ttScope ) + { + // Check if the expression is an anonymous function + if( IsLambda() ) + { + node->AddChildLast(ParseLambda()); + } + else + { + // Determine the last identifier in order to check if it is a type + sToken t; + if( t1.type == ttScope ) t = t2; else t = t1; + RewindTo(&t); + GetToken(&t2); + while( t.type == ttIdentifier ) + { + t2 = t; + GetToken(&t); + if( t.type == ttScope ) + GetToken(&t); + else + break; + } + + bool isDataType = IsDataType(t2); + bool isTemplateType = false; + if( isDataType ) + { + // Is this a template type? + tempString.Assign(&script->code[t2.pos], t2.length); + if( engine->IsTemplateType(tempString.AddressOf()) ) + isTemplateType = true; + } + + GetToken(&t2); + + // Rewind so the real parsing can be done, after deciding what to parse + RewindTo(&t1); + + // Check if this is a construct call + if( isDataType && (t.type == ttOpenParanthesis || // type() + (t.type == ttOpenBracket && t2.type == ttCloseBracket)) ) // type[]() + node->AddChildLast(ParseConstructCall()); + else if( isTemplateType && t.type == ttLessThan ) // type() + node->AddChildLast(ParseConstructCall()); + else if( IsFunctionCall() ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseVariableAccess()); + } + } + else if( t1.type == ttCast ) + node->AddChildLast(ParseCast()); + else if( IsConstant(t1.type) ) + node->AddChildLast(ParseConstant()); + else if( t1.type == ttOpenParanthesis ) + { + GetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t1); + Error(InsteadFound(t1), &t1); + } + + node->UpdateSourcePos(t1.pos, t1.length); + } + else + { + Error(TXT_EXPECTED_EXPRESSION_VALUE, &t1); + Error(InsteadFound(t1), &t1); + } + + return node; +} + +// BNF: LITERAL ::= NUMBER | STRING | BITS | 'true' | 'false' | 'null' +// BNF: NUMBER ::= single token: includes integers and real numbers, same as C++ +// BNF: STRING ::= single token: single quoted ', double quoted ", or heredoc multi-line string """ +// BNF: BITS ::= single token: binary 0b or 0B, octal 0o or 0O, decimal 0d or 0D, hexadecimal 0x or 0X +asCScriptNode *asCParser::ParseConstant() +{ + asCScriptNode *node = CreateNode(snConstant); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( !IsConstant(t.type) ) + { + Error(TXT_EXPECTED_CONSTANT, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + // We want to gather a list of string constants to concatenate as children + if( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant ) + RewindTo(&t); + + while( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant ) + { + node->AddChildLast(ParseStringConstant()); + + GetToken(&t); + RewindTo(&t); + } + + return node; +} + +bool asCParser::IsLambda() +{ + bool isLambda = false; + sToken t; + GetToken(&t); + if( t.type == ttIdentifier && IdentifierIs(t, FUNCTION_TOKEN) ) + { + sToken t2; + GetToken(&t2); + if( t2.type == ttOpenParanthesis ) + { + // Skip until ) + while( t2.type != ttCloseParanthesis && t2.type != ttEnd ) + GetToken(&t2); + + // The next token must be a { + GetToken(&t2); + if( t2.type == ttStartStatementBlock ) + isLambda = true; + } + } + + RewindTo(&t); + return isLambda; +} + +// BNF: LAMBDA ::= 'function' '(' [IDENTIFIER {',' IDENTIFIER}] ')' STATBLOCK +asCScriptNode *asCParser::ParseLambda() +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + if( t.type != ttIdentifier || !IdentifierIs(t, FUNCTION_TOKEN) ) + { + Error(ExpectedToken("function"), &t); + return node; + } + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + return node; + } + + GetToken(&t); + if( t.type == ttIdentifier ) + { + RewindTo(&t); + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + while( t.type == ttListSeparator ) + { + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t); + } + } + + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + return node; + } + + // We should just find the end of the statement block here. The statements + // will be parsed on request by the compiler once it starts the compilation. + node->AddChildLast(SuperficiallyParseStatementBlock()); + + return node; +} + +asCScriptNode *asCParser::ParseStringConstant() +{ + asCScriptNode *node = CreateNode(snConstant); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttStringConstant && t.type != ttMultilineStringConstant && t.type != ttHeredocStringConstant ) + { + Error(TXT_EXPECTED_STRING, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: FUNCCALL ::= SCOPE IDENTIFIER ARGLIST +asCScriptNode *asCParser::ParseFunctionCall() +{ + asCScriptNode *node = CreateNode(snFunctionCall); + if( node == 0 ) return 0; + + // Parse scope prefix + ParseOptionalScope(node); + + // Parse the function name followed by the argument list + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseArgList()); + + return node; +} + +// BNF: VARACCESS ::= SCOPE IDENTIFIER +asCScriptNode *asCParser::ParseVariableAccess() +{ + asCScriptNode *node = CreateNode(snVariableAccess); + if( node == 0 ) return 0; + + // Parse scope prefix + ParseOptionalScope(node); + + // Parse the variable name + node->AddChildLast(ParseIdentifier()); + + return node; +} + +// BNF: CONSTRUCTCALL ::= TYPE ARGLIST +asCScriptNode *asCParser::ParseConstructCall() +{ + asCScriptNode *node = CreateNode(snConstructCall); + if( node == 0 ) return 0; + + node->AddChildLast(ParseType(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseArgList()); + + return node; +} + +// BNF: ARGLIST ::= '(' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':'] ASSIGN} ')' +asCScriptNode *asCParser::ParseArgList(bool withParenthesis) +{ + asCScriptNode *node = CreateNode(snArgList); + if( node == 0 ) return 0; + + sToken t1; + if( withParenthesis ) + { + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + } + + GetToken(&t1); + if( t1.type == ttCloseParanthesis || t1.type == ttCloseBracket ) + { + if( withParenthesis ) + { + if( t1.type == ttCloseParanthesis ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(ttCloseBracket)); + + Error(str.AddressOf(), &t1); + } + } + else + RewindTo(&t1); + + // Argument list has ended + return node; + } + else + { + RewindTo(&t1); + + for(;;) + { + // Determine if this is a named argument + sToken tl, t2; + GetToken(&tl); + GetToken(&t2); + RewindTo(&tl); + + // Named arguments uses the syntax: arg : expr + // This avoids confusion when the argument has the same name as a local variable, i.e. var = expr + // It also avoids conflict with expressions to that creates anonymous objects initialized with lists, i.e. type = {...} + // The alternate syntax: arg = expr, is supported to provide backwards compatibility with 2.29.0 + // TODO: 3.0.0: Remove the alternate syntax + if( tl.type == ttIdentifier && (t2.type == ttColon || (engine->ep.alterSyntaxNamedArgs && t2.type == ttAssignment)) ) + { + asCScriptNode *named = CreateNode(snNamedArgument); + if( named == 0 ) return 0; + node->AddChildLast(named); + + named->AddChildLast(ParseIdentifier()); + GetToken(&t2); + + if( engine->ep.alterSyntaxNamedArgs == 1 && t2.type == ttAssignment ) + Warning(TXT_NAMED_ARGS_WITH_OLD_SYNTAX, &t2); + + named->AddChildLast(ParseAssignment()); + } + else + node->AddChildLast(ParseAssignment()); + + if( isSyntaxError ) return node; + + // Check if list continues + GetToken(&t1); + if( t1.type == ttListSeparator ) + continue; + else + { + if( withParenthesis ) + { + if( t1.type == ttCloseParanthesis ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + Error(ExpectedTokens(")", ","), &t1); + Error(InsteadFound(t1), &t1); + } + } + else + RewindTo(&t1); + + return node; + } + } + } +} + +bool asCParser::IsFunctionCall() +{ + sToken s; + sToken t1, t2; + + GetToken(&s); + t1 = s; + + // A function call may be prefixed with scope resolution + if( t1.type == ttScope ) + GetToken(&t1); + GetToken(&t2); + + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + + // A function call starts with an identifier followed by an argument list + if( t1.type != ttIdentifier || IsDataType(t1) ) + { + RewindTo(&s); + return false; + } + + if( t2.type == ttOpenParanthesis ) + { + RewindTo(&s); + return true; + } + + RewindTo(&s); + return false; +} + +// BNF: ASSIGN ::= CONDITION [ ASSIGNOP ASSIGN ] +asCScriptNode *asCParser::ParseAssignment() +{ + asCScriptNode *node = CreateNode(snAssignment); + if( node == 0 ) return 0; + + node->AddChildLast(ParseCondition()); + if( isSyntaxError ) return node; + + sToken t; + GetToken(&t); + RewindTo(&t); + + if( IsAssignOperator(t.type) ) + { + node->AddChildLast(ParseAssignOperator()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + + return node; +} + +// BNF: CONDITION ::= EXPR ['?' ASSIGN ':' ASSIGN] +asCScriptNode *asCParser::ParseCondition() +{ + asCScriptNode *node = CreateNode(snCondition); + if( node == 0 ) return 0; + + node->AddChildLast(ParseExpression()); + if( isSyntaxError ) return node; + + sToken t; + GetToken(&t); + if( t.type == ttQuestion ) + { + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttColon ) + { + Error(ExpectedToken(":"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + else + RewindTo(&t); + + return node; +} + +// BNF: EXPR ::= EXPRTERM {EXPROP EXPRTERM} +asCScriptNode *asCParser::ParseExpression() +{ + asCScriptNode *node = CreateNode(snExpression); + if( node == 0 ) return 0; + + node->AddChildLast(ParseExprTerm()); + if( isSyntaxError ) return node; + + for(;;) + { + sToken t; + GetToken(&t); + RewindTo(&t); + + if( !IsOperator(t.type) ) + return node; + + node->AddChildLast(ParseExprOperator()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseExprTerm()); + if( isSyntaxError ) return node; + } + UNREACHABLE_RETURN; +} + +// BNF: EXPRTERM ::= (TYPE '=' INITLIST) | ({EXPRPREOP} EXPRVALUE {EXPRPOSTOP}) +asCScriptNode *asCParser::ParseExprTerm() +{ + asCScriptNode *node = CreateNode(snExprTerm); + if( node == 0 ) return 0; + + // Check if the expression term is an initialization of a temp object with init list, i.e. type = {...} + sToken t; + GetToken(&t); + sToken t2 = t, t3; + if (IsDataType(t2) && CheckTemplateType(t2)) + { + // The next token must be a = followed by a { + GetToken(&t2); + GetToken(&t3); + if (t2.type == ttAssignment && t3.type == ttStartStatementBlock) + { + // It is an initialization, now parse it for real + RewindTo(&t); + node->AddChildLast(ParseType(false)); + GetToken(&t2); + node->AddChildLast(ParseInitList()); + return node; + } + } + + // It wasn't an initialization, so it must be an ordinary expression term + RewindTo(&t); + + for(;;) + { + GetToken(&t); + RewindTo(&t); + if( !IsPreOperator(t.type) ) + break; + + node->AddChildLast(ParseExprPreOp()); + if( isSyntaxError ) return node; + } + + node->AddChildLast(ParseExprValue()); + if( isSyntaxError ) return node; + + + for(;;) + { + GetToken(&t); + RewindTo(&t); + if( !IsPostOperator(t.type) ) + return node; + + node->AddChildLast(ParseExprPostOp()); + if( isSyntaxError ) return node; + } + UNREACHABLE_RETURN; +} + +// BNF: EXPRPREOP ::= '-' | '+' | '!' | '++' | '--' | '~' | '@' +asCScriptNode *asCParser::ParseExprPreOp() +{ + asCScriptNode *node = CreateNode(snExprPreOp); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( !IsPreOperator(t.type) ) + { + Error(TXT_EXPECTED_PRE_OPERATOR, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: EXPRPOSTOP ::= ('.' (FUNCCALL | IDENTIFIER)) | ('[' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':' ASSIGN} ']') | ARGLIST | '++' | '--' +asCScriptNode *asCParser::ParseExprPostOp() +{ + asCScriptNode *node = CreateNode(snExprPostOp); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( !IsPostOperator(t.type) ) + { + Error(TXT_EXPECTED_POST_OPERATOR, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + if( t.type == ttDot ) + { + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + if( t2.type == ttOpenParanthesis ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseIdentifier()); + } + else if( t.type == ttOpenBracket ) + { + node->AddChildLast(ParseArgList(false)); + + GetToken(&t); + if( t.type != ttCloseBracket ) + { + Error(ExpectedToken("]"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + } + else if( t.type == ttOpenParanthesis ) + { + RewindTo(&t); + node->AddChildLast(ParseArgList()); + } + + return node; +} + +// BNF: EXPROP ::= MATHOP | COMPOP | LOGICOP | BITOP +// BNF: MATHOP ::= '+' | '-' | '*' | '/' | '%' | '**' +// BNF: COMPOP ::= '==' | '!=' | '<' | '<=' | '>' | '>=' | 'is' | '!is' +// BNF: LOGICOP ::= '&&' | '||' | '^^' | 'and' | 'or' | 'xor' +// BNF: BITOP ::= '&' | '|' | '^' | '<<' | '>>' | '>>>' +asCScriptNode *asCParser::ParseExprOperator() +{ + asCScriptNode *node = CreateNode(snExprOperator); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( !IsOperator(t.type) ) + { + Error(TXT_EXPECTED_OPERATOR, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: ASSIGNOP ::= '=' | '+=' | '-=' | '*=' | '/=' | '|=' | '&=' | '^=' | '%=' | '**=' | '<<=' | '>>=' | '>>>=' +asCScriptNode *asCParser::ParseAssignOperator() +{ + asCScriptNode *node = CreateNode(snExprOperator); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( !IsAssignOperator(t.type) ) + { + Error(TXT_EXPECTED_OPERATOR, &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +bool asCParser::IsOperator(int tokenType) +{ + if( tokenType == ttPlus || + tokenType == ttMinus || + tokenType == ttStar || + tokenType == ttSlash || + tokenType == ttPercent || + tokenType == ttStarStar || + tokenType == ttAnd || + tokenType == ttOr || + tokenType == ttXor || + tokenType == ttEqual || + tokenType == ttNotEqual || + tokenType == ttLessThan || + tokenType == ttLessThanOrEqual || + tokenType == ttGreaterThan || + tokenType == ttGreaterThanOrEqual || + tokenType == ttAmp || + tokenType == ttBitOr || + tokenType == ttBitXor || + tokenType == ttBitShiftLeft || + tokenType == ttBitShiftRight || + tokenType == ttBitShiftRightArith || + tokenType == ttIs || + tokenType == ttNotIs ) + return true; + + return false; +} + +bool asCParser::IsAssignOperator(int tokenType) +{ + if( tokenType == ttAssignment || + tokenType == ttAddAssign || + tokenType == ttSubAssign || + tokenType == ttMulAssign || + tokenType == ttDivAssign || + tokenType == ttModAssign || + tokenType == ttPowAssign || + tokenType == ttAndAssign || + tokenType == ttOrAssign || + tokenType == ttXorAssign || + tokenType == ttShiftLeftAssign || + tokenType == ttShiftRightLAssign || + tokenType == ttShiftRightAAssign ) + return true; + + return false; +} + +bool asCParser::IsPreOperator(int tokenType) +{ + if( tokenType == ttMinus || + tokenType == ttPlus || + tokenType == ttNot || + tokenType == ttInc || + tokenType == ttDec || + tokenType == ttBitNot || + tokenType == ttHandle ) + return true; + return false; +} + +bool asCParser::IsPostOperator(int tokenType) +{ + if( tokenType == ttInc || // post increment + tokenType == ttDec || // post decrement + tokenType == ttDot || // member access + tokenType == ttOpenBracket || // index operator + tokenType == ttOpenParanthesis ) // argument list for call on function pointer + return true; + return false; +} + +bool asCParser::IsConstant(int tokenType) +{ + if( tokenType == ttIntConstant || + tokenType == ttFloatConstant || + tokenType == ttDoubleConstant || + tokenType == ttStringConstant || + tokenType == ttMultilineStringConstant || + tokenType == ttHeredocStringConstant || + tokenType == ttTrue || + tokenType == ttFalse || + tokenType == ttBitsConstant || + tokenType == ttNull ) + return true; + + return false; +} + +int asCParser::ParseScript(asCScriptCode *in_script) +{ + Reset(); + + this->script = in_script; + + scriptNode = ParseScript(false); + + if( errorWhileParsing ) + return -1; + + // TODO: Should allow application to request this warning to be generated. + // It should be off by default, since pre-processor may remove all + // code from a section while still being meant as valid code +/* + // Warn in case there isn't anything in the script + if( scriptNode->firstChild == 0 ) + { + if( builder ) + builder->WriteWarning(script->name, TXT_SECTION_IS_EMPTY, 1, 1); + } +*/ + return 0; +} + +int asCParser::ParseExpression(asCScriptCode *in_script) +{ + Reset(); + + this->script = in_script; + + checkValidTypes = true; + + scriptNode = ParseExpression(); + if( errorWhileParsing ) + return -1; + + return 0; +} + +// BNF: IMPORT ::= 'import' TYPE ['&'] IDENTIFIER PARAMLIST 'from' STRING ';' +asCScriptNode *asCParser::ParseImport() +{ + asCScriptNode *node = CreateNode(snImport); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttImport ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttImport)), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + node->AddChildLast(ParseFunctionDefinition()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttIdentifier ) + { + Error(ExpectedToken(FROM_TOKEN), &t); + Error(InsteadFound(t), &t); + return node; + } + + tempString.Assign(&script->code[t.pos], t.length); + if( tempString != FROM_TOKEN ) + { + Error(ExpectedToken(FROM_TOKEN), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttStringConstant ) + { + Error(TXT_EXPECTED_STRING, &t); + Error(InsteadFound(t), &t); + return node; + } + + asCScriptNode *mod = CreateNode(snConstant); + if( mod == 0 ) return 0; + + node->AddChildLast(mod); + + mod->SetToken(&t); + mod->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: SCRIPT ::= {IMPORT | ENUM | TYPEDEF | CLASS | MIXIN | INTERFACE | FUNCDEF | VIRTPROP | VAR | FUNC | NAMESPACE | ';'} +asCScriptNode *asCParser::ParseScript(bool inBlock) +{ + asCScriptNode *node = CreateNode(snScript); + if( node == 0 ) return 0; + + // Determine type of node + sToken t1, t2; + + for(;;) + { + while( !isSyntaxError ) + { + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + if( t1.type == ttImport ) + node->AddChildLast(ParseImport()); + else if( t1.type == ttEnum || (IdentifierIs(t1, SHARED_TOKEN) && t2.type == ttEnum) ) + node->AddChildLast(ParseEnumeration()); // Handle enumerations + else if( t1.type == ttTypedef ) + node->AddChildLast(ParseTypedef()); // Handle primitive typedefs + else if( t1.type == ttClass || + ((IdentifierIs(t1, SHARED_TOKEN) || IdentifierIs(t1, FINAL_TOKEN) || IdentifierIs(t1, ABSTRACT_TOKEN)) && t2.type == ttClass) || + (IdentifierIs(t1, SHARED_TOKEN) && (IdentifierIs(t2, FINAL_TOKEN) || IdentifierIs(t2, ABSTRACT_TOKEN))) ) + node->AddChildLast(ParseClass()); + else if( t1.type == ttMixin ) + node->AddChildLast(ParseMixin()); + else if( t1.type == ttInterface || (t1.type == ttIdentifier && t2.type == ttInterface) ) + node->AddChildLast(ParseInterface()); + else if( t1.type == ttFuncDef ) + node->AddChildLast(ParseFuncDef()); + else if( t1.type == ttConst || t1.type == ttScope || t1.type == ttAuto || IsDataType(t1) ) + { + if( IsVirtualPropertyDecl() ) + node->AddChildLast(ParseVirtualPropertyDecl(false, false)); + else if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration(false, true)); + else + node->AddChildLast(ParseFunction()); + } + else if( t1.type == ttEndStatement ) + { + // Ignore a semicolon by itself + GetToken(&t1); + } + else if( t1.type == ttNamespace ) + node->AddChildLast(ParseNamespace()); + else if( t1.type == ttEnd ) + return node; + else if( inBlock && t1.type == ttEndStatementBlock ) + return node; + else + { + asCString str; + const char *t = asCTokenizer::GetDefinition(t1.type); + if( t == 0 ) t = ""; + + str.Format(TXT_UNEXPECTED_TOKEN_s, t); + + Error(str, &t1); + } + } + + if( isSyntaxError ) + { + // Search for either ';' or '{' or end + GetToken(&t1); + while( t1.type != ttEndStatement && t1.type != ttEnd && + t1.type != ttStartStatementBlock ) + GetToken(&t1); + + if( t1.type == ttStartStatementBlock ) + { + // Find the end of the block and skip nested blocks + int level = 1; + while( level > 0 ) + { + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) level++; + if( t1.type == ttEndStatementBlock ) level--; + if( t1.type == ttEnd ) break; + } + } + + isSyntaxError = false; + } + } + UNREACHABLE_RETURN; +} + +// BNF: NAMESPACE ::= 'namespace' IDENTIFIER '{' SCRIPT '}' +asCScriptNode *asCParser::ParseNamespace() +{ + asCScriptNode *node = CreateNode(snNamespace); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type == ttNamespace ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttNamespace)), &t1); + Error(InsteadFound(t1), &t1); + } + + // TODO: namespace: Allow declaration of multiple nested namespace with namespace A::B::C { } + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttStartStatementBlock)), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + sToken start = t1; + + node->AddChildLast(ParseScript(true)); + + if( !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + if( t1.type == ttEnd ) + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + else + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatementBlock)), &t1); + Error(InsteadFound(t1), &t1); + } + Info(TXT_WHILE_PARSING_NAMESPACE, &start); + return node; + } + } + + return node; +} + +int asCParser::ParseStatementBlock(asCScriptCode *in_script, asCScriptNode *in_block) +{ + TimeIt("asCParser::ParseStatementBlock"); + + Reset(); + + // Tell the parser to validate the identifiers as valid types + checkValidTypes = true; + + this->script = in_script; + sourcePos = in_block->tokenPos; + + scriptNode = ParseStatementBlock(); + + if( isSyntaxError || errorWhileParsing ) + return -1; + + return 0; +} + +// BNF: ENUM ::= ['shared'] 'enum' IDENTIFIER '{' IDENTIFIER ['=' EXPR] {',' IDENTIFIER ['=' EXPR]} '}' +asCScriptNode *asCParser::ParseEnumeration() +{ + asCScriptNode *ident; + asCScriptNode *dataType; + + asCScriptNode *node = CreateNode(snEnum); + if( node == 0 ) return 0; + + sToken token; + + // Optional 'shared' token + GetToken(&token); + if( IdentifierIs(token, SHARED_TOKEN) ) + { + RewindTo(&token); + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&token); + } + + // Check for enum + if( token.type != ttEnum ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnum)), &token); + Error(InsteadFound(token), &token); + return node; + } + + node->SetToken(&token); + node->UpdateSourcePos(token.pos, token.length); + + // Get the identifier + GetToken(&token); + if(ttIdentifier != token.type) + { + Error(TXT_EXPECTED_IDENTIFIER, &token); + Error(InsteadFound(token), &token); + return node; + } + + dataType = CreateNode(snDataType); + if( dataType == 0 ) return 0; + + node->AddChildLast(dataType); + + ident = CreateNode(snIdentifier); + if( ident == 0 ) return 0; + + ident->SetToken(&token); + ident->UpdateSourcePos(token.pos, token.length); + dataType->AddChildLast(ident); + + // check for the start of the declaration block + GetToken(&token); + if( token.type != ttStartStatementBlock ) + { + RewindTo(&token); + Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(InsteadFound(token), &token); + return node; + } + + while(ttEnd != token.type) + { + GetToken(&token); + + if( ttEndStatementBlock == token.type ) + { + RewindTo(&token); + break; + } + + if(ttIdentifier != token.type) + { + Error(TXT_EXPECTED_IDENTIFIER, &token); + Error(InsteadFound(token), &token); + return node; + } + + // Add the enum element + ident = CreateNode(snIdentifier); + if( ident == 0 ) return 0; + + ident->SetToken(&token); + ident->UpdateSourcePos(token.pos, token.length); + node->AddChildLast(ident); + + GetToken(&token); + + if( token.type == ttAssignment ) + { + asCScriptNode *tmp; + + RewindTo(&token); + + tmp = SuperficiallyParseVarInit(); + + node->AddChildLast(tmp); + if( isSyntaxError ) return node; + GetToken(&token); + } + + if(ttListSeparator != token.type) + { + RewindTo(&token); + break; + } + } + + // check for the end of the declaration block + GetToken(&token); + if( token.type != ttEndStatementBlock ) + { + RewindTo(&token); + Error(ExpectedToken("}"), &token); + Error(InsteadFound(token), &token); + return node; + } + + // Parse the declarations + return node; +} + +bool asCParser::IsVarDecl() +{ + // Set start point so that we can rewind + sToken t; + GetToken(&t); + RewindTo(&t); + + // A class property decl can be preceded by 'private' or 'protected' + sToken t1; + GetToken(&t1); + if( t1.type != ttPrivate && t1.type != ttProtected ) + RewindTo(&t1); + + // A variable decl can start with a const + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + sToken t2; + if( t1.type != ttAuto ) + { + // The type may be initiated with the scope operator + if( t1.type == ttScope ) + GetToken(&t1); + + // The type may be preceeded with a multilevel scope + GetToken(&t2); + while( t1.type == ttIdentifier ) + { + if (t2.type == ttScope) + { + GetToken(&t1); + GetToken(&t2); + continue; + } + else if(t2.type == ttLessThan) + { + // Template types can also be used as scope identifiers + RewindTo(&t2); + if (CheckTemplateType(t1)) + { + sToken t3; + GetToken(&t3); + if (t3.type == ttScope) + { + GetToken(&t1); + GetToken(&t2); + continue; + } + } + } + + break; + } + RewindTo(&t2); + } + + // We don't validate if the identifier is an actual declared type at this moment + // as it may wrongly identify the statement as a non-declaration if the user typed + // the name incorrectly. The real type is validated in ParseDeclaration where a + // proper error message can be given. + if( !IsRealType(t1.type) && t1.type != ttIdentifier && t1.type != ttAuto ) + { + RewindTo(&t); + return false; + } + + if( !CheckTemplateType(t1) ) + { + RewindTo(&t); + return false; + } + + // Object handles can be interleaved with the array brackets + // Even though declaring variables with & is invalid we'll accept + // it here to give an appropriate error message later + GetToken(&t2); + while( t2.type == ttHandle || t2.type == ttAmp || t2.type == ttOpenBracket ) + { + if( t2.type == ttOpenBracket ) + { + GetToken(&t2); + if( t2.type != ttCloseBracket ) + { + RewindTo(&t); + return false; + } + } + + GetToken(&t2); + } + + if( t2.type != ttIdentifier ) + { + RewindTo(&t); + return false; + } + + GetToken(&t2); + if( t2.type == ttEndStatement || t2.type == ttAssignment || t2.type == ttListSeparator ) + { + RewindTo(&t); + return true; + } + if( t2.type == ttOpenParanthesis ) + { + // If the closing paranthesis is followed by a statement + // block or end-of-file, then treat it as a function. A + // function decl may have nested paranthesis so we need to + // check for this too. + int nest = 0; + while( t2.type != ttEnd ) + { + if( t2.type == ttOpenParanthesis ) + nest++; + else if( t2.type == ttCloseParanthesis ) + { + nest--; + if( nest == 0 ) + break; + } + GetToken(&t2); + } + + if( t2.type == ttEnd ) + return false; + else + { + GetToken(&t1); + RewindTo(&t); + if( t1.type == ttStartStatementBlock || t1.type == ttEnd ) + return false; + } + + RewindTo(&t); + + return true; + } + + RewindTo(&t); + return false; +} + +bool asCParser::IsVirtualPropertyDecl() +{ + // Set start point so that we can rewind + sToken t; + GetToken(&t); + RewindTo(&t); + + // A class property decl can be preceded by 'private' or 'protected' + sToken t1; + GetToken(&t1); + if( t1.type != ttPrivate && t1.type != ttProtected ) + RewindTo(&t1); + + // A variable decl can start with a const + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + // We don't validate if the identifier is an actual declared type at this moment + // as it may wrongly identify the statement as a non-declaration if the user typed + // the name incorrectly. The real type is validated in ParseDeclaration where a + // proper error message can be given. + if( t1.type == ttScope ) + GetToken(&t1); + + if( t1.type == ttIdentifier ) + { + sToken t2; + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + + RewindTo(&t2); + } + else if( !IsRealType(t1.type) ) + { + RewindTo(&t); + return false; + } + + if( !CheckTemplateType(t1) ) + { + RewindTo(&t); + return false; + } + + // Object handles can be interleaved with the array brackets + sToken t2; + GetToken(&t2); + while( t2.type == ttHandle || t2.type == ttOpenBracket ) + { + if( t2.type == ttOpenBracket ) + { + GetToken(&t2); + if( t2.type != ttCloseBracket ) + { + RewindTo(&t); + return false; + } + } + + GetToken(&t2); + } + + if( t2.type != ttIdentifier ) + { + RewindTo(&t); + return false; + } + + GetToken(&t2); + if( t2.type == ttStartStatementBlock ) + { + RewindTo(&t); + return true; + } + + RewindTo(&t); + return false; +} + +bool asCParser::IsFuncDecl(bool isMethod) +{ + // Set start point so that we can rewind + sToken t; + GetToken(&t); + RewindTo(&t); + + if( isMethod ) + { + // A class method decl can be preceded by 'private' or 'protected' + sToken t1, t2; + GetToken(&t1); + if( t1.type != ttPrivate && t1.type != ttProtected ) + RewindTo(&t1); + + // A class constructor starts with identifier followed by parenthesis + // A class destructor starts with the ~ token + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + if( (t1.type == ttIdentifier && t2.type == ttOpenParanthesis) || t1.type == ttBitNot ) + { + RewindTo(&t); + return true; + } + } + + // A function decl can start with a const + sToken t1; + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + // The return type can be optionally preceeded by a scope + if( t1.type == ttScope ) + GetToken(&t1); + while( t1.type == ttIdentifier ) + { + sToken t2; + GetToken(&t2); + if( t2.type == ttScope ) + GetToken(&t1); + else + { + RewindTo(&t2); + break; + } + } + + if( !IsDataType(t1) ) + { + RewindTo(&t); + return false; + } + + // If the type is a template type, then skip the angle brackets holding the subtype + if( !CheckTemplateType(t1) ) + { + RewindTo(&t); + return false; + } + + // Object handles can be interleaved with the array brackets + sToken t2; + GetToken(&t2); + while( t2.type == ttHandle || t2.type == ttOpenBracket ) + { + if( t2.type == ttOpenBracket ) + { + GetToken(&t2); + if( t2.type != ttCloseBracket ) + { + RewindTo(&t); + return false; + } + } + + GetToken(&t2); + } + + // There can be an ampersand if the function returns a reference + if( t2.type == ttAmp ) + { + RewindTo(&t); + return true; + } + + if( t2.type != ttIdentifier ) + { + RewindTo(&t); + return false; + } + + GetToken(&t2); + if( t2.type == ttOpenParanthesis ) + { + // If the closing parenthesis is not followed by a + // statement block then it is not a function. + // It's possible that there are nested parenthesis due to default + // arguments so this should be checked for. + int nest = 0; + GetToken(&t2); + while( (nest || t2.type != ttCloseParanthesis) && t2.type != ttEnd ) + { + if( t2.type == ttOpenParanthesis ) + nest++; + if( t2.type == ttCloseParanthesis ) + nest--; + + GetToken(&t2); + } + + if( t2.type == ttEnd ) + return false; + else + { + if( isMethod ) + { + // A class method can have a 'const' token after the parameter list + GetToken(&t1); + if( t1.type != ttConst ) + RewindTo(&t1); + + // A class method may also have any number of additional inheritance behavior specifiers + for( ; ; ) + { + GetToken(&t2); + if( !IdentifierIs(t2, FINAL_TOKEN) && !IdentifierIs(t2, OVERRIDE_TOKEN) ) + { + RewindTo(&t2); + break; + } + } + } + + GetToken(&t1); + RewindTo(&t); + if( t1.type == ttStartStatementBlock ) + return true; + } + + RewindTo(&t); + return false; + } + + RewindTo(&t); + return false; +} + +// BNF: FUNCDEF ::= 'funcdef' TYPE ['&'] IDENTIFIER PARAMLIST ';' +asCScriptNode *asCParser::ParseFuncDef() +{ + asCScriptNode *node = CreateNode(snFuncDef); + if( node == 0 ) return 0; + + sToken t1; + GetToken(&t1); + if( t1.type != ttFuncDef ) + { + Error(asCTokenizer::GetDefinition(ttFuncDef), &t1); + return node; + } + + node->SetToken(&t1); + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttEndStatement ) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: FUNC ::= ['private' | 'protected' | 'shared'] [((TYPE ['&']) | '~')] IDENTIFIER PARAMLIST ['const'] {'override' | 'final'} STATBLOCK +asCScriptNode *asCParser::ParseFunction(bool isMethod) +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + sToken t1,t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + // A class method can start with 'private' or 'protected' + if( isMethod && t1.type == ttPrivate ) + node->AddChildLast(ParseToken(ttPrivate)); + else if( isMethod && t1.type == ttProtected ) + node->AddChildLast(ParseToken(ttProtected)); + if( isSyntaxError ) return node; + + // A global function can be marked as shared + if( !isMethod && IdentifierIs(t1, SHARED_TOKEN) ) + { + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + } + + // If it is a global function, or a method, except constructor and destructor, then the return type is parsed + if( !isMethod || (t1.type != ttBitNot && t2.type != ttOpenParanthesis) ) + { + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + } + + // If this is a class destructor then it starts with ~, and no return type is declared + if( isMethod && t1.type == ttBitNot ) + { + node->AddChildLast(ParseToken(ttBitNot)); + if( isSyntaxError ) return node; + } + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + if( isMethod ) + { + GetToken(&t1); + RewindTo(&t1); + + // Is the method a const? + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + + // TODO: Should support abstract methods, in which case no statement block should be provided + ParseMethodOverrideBehaviors(node); + if( isSyntaxError ) return node; + } + + // We should just find the end of the statement block here. The statements + // will be parsed on request by the compiler once it starts the compilation. + node->AddChildLast(SuperficiallyParseStatementBlock()); + + return node; +} + +// BNF: INTFMTHD ::= TYPE ['&'] IDENTIFIER PARAMLIST ['const'] ';' +asCScriptNode *asCParser::ParseInterfaceMethod() +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + // Parse an optional const after the method definition + sToken t1; + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + + GetToken(&t1); + if( t1.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: VIRTPROP ::= ['private' | 'protected'] TYPE ['&'] IDENTIFIER '{' {('get' | 'set') ['const'] [('override' | 'final')] (STATBLOCK | ';')} '}' +asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterface) +{ + asCScriptNode *node = CreateNode(snVirtualProperty); + if( node == 0 ) return 0; + + sToken t1,t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + // A class method can start with 'private' or 'protected' + if( isMethod && t1.type == ttPrivate ) + node->AddChildLast(ParseToken(ttPrivate)); + else if( isMethod && t1.type == ttProtected ) + node->AddChildLast(ParseToken(ttProtected)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + for(;;) + { + GetToken(&t1); + asCScriptNode *accessorNode = 0; + + if( IdentifierIs(t1, GET_TOKEN) || IdentifierIs(t1, SET_TOKEN) ) + { + accessorNode = CreateNode(snVirtualProperty); + if( accessorNode == 0 ) return 0; + + node->AddChildLast(accessorNode); + + RewindTo(&t1); + accessorNode->AddChildLast(ParseIdentifier()); + + if( isMethod ) + { + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + accessorNode->AddChildLast(ParseToken(ttConst)); + + if( !isInterface ) + { + ParseMethodOverrideBehaviors(accessorNode); + if( isSyntaxError ) return node; + } + } + + if( !isInterface ) + { + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) + { + RewindTo(&t1); + accessorNode->AddChildLast(SuperficiallyParseStatementBlock()); + if( isSyntaxError ) return node; + } + else if( t1.type != ttEndStatement ) + { + Error(ExpectedTokens(";", "{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + else + { + GetToken(&t1); + if( t1.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + } + else if( t1.type == ttEndStatementBlock ) + break; + else + { + const char *tokens[] = { GET_TOKEN, SET_TOKEN, asCTokenizer::GetDefinition(ttEndStatementBlock) }; + Error(ExpectedOneOf(tokens, 3), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + + return node; +} + +// BNF: INTERFACE ::= ['shared'] 'interface' IDENTIFIER [':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | INTFMTHD} '}' +asCScriptNode *asCParser::ParseInterface() +{ + asCScriptNode *node = CreateNode(snInterface); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + // Allow keyword 'shared' before 'interface' + if( t.type == ttIdentifier ) + { + tempString.Assign(&script->code[t.pos], t.length); + if( tempString != SHARED_TOKEN ) + { + Error(ExpectedToken(SHARED_TOKEN), &t); + Error(InsteadFound(t), &t); + return node; + } + + RewindTo(&t); + node->AddChildLast(ParseIdentifier()); + GetToken(&t); + } + + if( t.type != ttInterface ) + { + Error(ExpectedToken("interface"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + + node->AddChildLast(ParseIdentifier()); + + // Can optionally have a list of interfaces that are inherited + GetToken(&t); + if( t.type == ttColon ) + { + asCScriptNode *inherit = CreateNode(snIdentifier); + node->AddChildLast(inherit); + + ParseOptionalScope(inherit); + inherit->AddChildLast(ParseIdentifier()); + GetToken(&t); + while( t.type == ttListSeparator ) + { + inherit = CreateNode(snIdentifier); + node->AddChildLast(inherit); + + ParseOptionalScope(inherit); + inherit->AddChildLast(ParseIdentifier()); + GetToken(&t); + } + } + + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); + return node; + } + + // Parse interface methods + GetToken(&t); + RewindTo(&t); + while( t.type != ttEndStatementBlock && t.type != ttEnd ) + { + if( IsVirtualPropertyDecl() ) + node->AddChildLast(ParseVirtualPropertyDecl(true, true)); + else if( t.type == ttEndStatement ) + // Skip empty declarations + GetToken(&t); + else + // Parse the method signature + node->AddChildLast(ParseInterfaceMethod()); + + if( isSyntaxError ) return node; + + GetToken(&t); + RewindTo(&t); + } + + GetToken(&t); + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: MIXIN ::= 'mixin' CLASS +asCScriptNode *asCParser::ParseMixin() +{ + asCScriptNode *node = CreateNode(snMixin); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + if( t.type != ttMixin ) + { + Error(ExpectedToken("mixin"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + + // A mixin token must be followed by a class declaration + node->AddChildLast(ParseClass()); + + return node; +} + +// BNF: CLASS ::= {'shared' | 'abstract' | 'final'} 'class' IDENTIFIER [':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | FUNC | VAR | FUNCDEF} '}' +asCScriptNode *asCParser::ParseClass() +{ + asCScriptNode *node = CreateNode(snClass); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + // Allow the keywords 'shared', 'abstract', and 'final' before 'class' + while( IdentifierIs(t, SHARED_TOKEN) || + IdentifierIs(t, ABSTRACT_TOKEN) || + IdentifierIs(t, FINAL_TOKEN) ) + { + RewindTo(&t); + node->AddChildLast(ParseIdentifier()); + GetToken(&t); + } + + if( t.type != ttClass ) + { + Error(ExpectedToken("class"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->SetToken(&t); + + if( engine->ep.allowImplicitHandleTypes ) + { + // Parse 'implicit handle class' construct + GetToken(&t); + + if ( t.type == ttHandle ) + node->SetToken(&t); + else + RewindTo(&t); + } + + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + + // Optional list of interfaces that are being implemented and classes that are being inherited + if( t.type == ttColon ) + { + asCScriptNode *inherit = CreateNode(snIdentifier); + node->AddChildLast(inherit); + + ParseOptionalScope(inherit); + inherit->AddChildLast(ParseIdentifier()); + GetToken(&t); + while( t.type == ttListSeparator ) + { + inherit = CreateNode(snIdentifier); + node->AddChildLast(inherit); + + ParseOptionalScope(inherit); + inherit->AddChildLast(ParseIdentifier()); + GetToken(&t); + } + } + + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); + return node; + } + + // Parse properties + GetToken(&t); + RewindTo(&t); + while( t.type != ttEndStatementBlock && t.type != ttEnd ) + { + // Is it a property or a method? + if (t.type == ttFuncDef) + node->AddChildLast(ParseFuncDef()); + else if( IsFuncDecl(true) ) + node->AddChildLast(ParseFunction(true)); + else if( IsVirtualPropertyDecl() ) + node->AddChildLast(ParseVirtualPropertyDecl(true, false)); + else if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration(true)); + else if( t.type == ttEndStatement ) + // Skip empty declarations + GetToken(&t); + else + { + Error(TXT_EXPECTED_METHOD_OR_PROPERTY, &t); + Error(InsteadFound(t), &t); + return node; + } + + if( isSyntaxError ) + return node; + + GetToken(&t); + RewindTo(&t); + } + + GetToken(&t); + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); + return node; + } + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +int asCParser::ParseVarInit(asCScriptCode *in_script, asCScriptNode *in_init) +{ + Reset(); + + // Tell the parser to validate the identifiers as valid types + checkValidTypes = true; + + this->script = in_script; + sourcePos = in_init->tokenPos; + + // If next token is assignment, parse expression + sToken t; + GetToken(&t); + if( t.type == ttAssignment ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttStartStatementBlock ) + scriptNode = ParseInitList(); + else + scriptNode = ParseAssignment(); + } + else if( t.type == ttOpenParanthesis ) + { + RewindTo(&t); + scriptNode = ParseArgList(); + } + else + { + int tokens[] = {ttAssignment, ttOpenParanthesis}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + } + + // Don't allow any more tokens after the expression + GetToken(&t); + if( t.type != ttEnd && t.type != ttEndStatement && t.type != ttListSeparator && t.type != ttEndStatementBlock ) + { + asCString msg; + msg.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(t.type)); + Error(msg, &t); + } + + if( isSyntaxError || errorWhileParsing ) + return -1; + + return 0; +} + +asCScriptNode *asCParser::SuperficiallyParseVarInit() +{ + asCScriptNode *node = CreateNode(snAssignment); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + if( t.type == ttAssignment ) + { + GetToken(&t); + sToken start = t; + + // Find the end of the expression + int indentParan = 0; + int indentBrace = 0; + while( indentParan || indentBrace || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) + { + if( t.type == ttOpenParanthesis ) + indentParan++; + else if( t.type == ttCloseParanthesis ) + indentParan--; + else if( t.type == ttStartStatementBlock ) + indentBrace++; + else if( t.type == ttEndStatementBlock ) + indentBrace--; + else if( t.type == ttNonTerminatedStringConstant ) + { + Error(TXT_NONTERMINATED_STRING, &t); + break; + } + else if( t.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + Info(TXT_WHILE_PARSING_EXPRESSION, &start); + break; + } + GetToken(&t); + } + + // Rewind so that the next token read is the list separator, end statement, or end statement block + RewindTo(&t); + } + else if( t.type == ttOpenParanthesis ) + { + sToken start = t; + + // Find the end of the argument list + int indent = 1; + while( indent ) + { + GetToken(&t); + if( t.type == ttOpenParanthesis ) + indent++; + else if( t.type == ttCloseParanthesis ) + indent--; + else if( t.type == ttNonTerminatedStringConstant ) + { + Error(TXT_NONTERMINATED_STRING, &t); + break; + } + else if( t.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + Info(TXT_WHILE_PARSING_ARG_LIST, &start); + break; + } + } + } + else + { + int tokens[] = {ttAssignment, ttOpenParanthesis}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + } + + return node; +} + +asCScriptNode *asCParser::SuperficiallyParseStatementBlock() +{ + asCScriptNode *node = CreateNode(snStatementBlock); + if( node == 0 ) return 0; + + // This function will only superficially parse the statement block in order to find the end of it + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + sToken start = t1; + + int level = 1; + while( level > 0 && !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + level--; + else if( t1.type == ttStartStatementBlock ) + level++; + else if( t1.type == ttNonTerminatedStringConstant ) + { + Error(TXT_NONTERMINATED_STRING, &t1); + break; + } + else if( t1.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start); + break; + } + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +// BNF: STATBLOCK ::= '{' {VAR | STATEMENT} '}' +asCScriptNode *asCParser::ParseStatementBlock() +{ + asCScriptNode *node = CreateNode(snStatementBlock); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + sToken start = t1; + + node->UpdateSourcePos(t1.pos, t1.length); + + for(;;) + { + while( !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + + if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration()); + else + node->AddChildLast(ParseStatement()); + } + } + + if( isSyntaxError ) + { + // Search for either ';', '{', '}', or end + GetToken(&t1); + while( t1.type != ttEndStatement && t1.type != ttEnd && + t1.type != ttStartStatementBlock && t1.type != ttEndStatementBlock ) + { + GetToken(&t1); + } + + // Skip this statement block + if( t1.type == ttStartStatementBlock ) + { + // Find the end of the block and skip nested blocks + int level = 1; + while( level > 0 ) + { + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) level++; + if( t1.type == ttEndStatementBlock ) level--; + if( t1.type == ttEnd ) break; + } + } + else if( t1.type == ttEndStatementBlock ) + { + RewindTo(&t1); + } + else if( t1.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start); + return node; + } + + isSyntaxError = false; + } + } + UNREACHABLE_RETURN; +} + +// BNF: INITLIST ::= '{' [ASSIGN | INITLIST] {',' [ASSIGN | INITLIST]} '}' +asCScriptNode *asCParser::ParseInitList() +{ + asCScriptNode *node = CreateNode(snInitList); + if( node == 0 ) return 0; + + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + for(;;) + { + GetToken(&t1); + if( t1.type == ttListSeparator ) + { + // No expression + node->AddChildLast(CreateNode(snUndefined)); + node->lastChild->UpdateSourcePos(t1.pos, 1); + + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + // No expression + node->AddChildLast(CreateNode(snUndefined)); + node->lastChild->UpdateSourcePos(t1.pos, 1); + node->UpdateSourcePos(t1.pos, t1.length); + return node; + } + RewindTo(&t1); + } + else if( t1.type == ttEndStatementBlock ) + { + // No expression + node->AddChildLast(CreateNode(snUndefined)); + node->lastChild->UpdateSourcePos(t1.pos, 1); + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else if( t1.type == ttStartStatementBlock ) + { + RewindTo(&t1); + node->AddChildLast(ParseInitList()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type == ttListSeparator ) + continue; + else if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + Error(ExpectedTokens("}", ","), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + else + { + RewindTo(&t1); + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + + GetToken(&t1); + if( t1.type == ttListSeparator ) + continue; + else if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + Error(ExpectedTokens("}", ","), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + } + } + } + UNREACHABLE_RETURN; +} + +// BNF: VAR ::= ['private'|'protected'] TYPE IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST] {',' IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST]} ';' +asCScriptNode *asCParser::ParseDeclaration(bool isClassProp, bool isGlobalVar) +{ + asCScriptNode *node = CreateNode(snDeclaration); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + RewindTo(&t); + + // A class property can be preceeded by private + if( t.type == ttPrivate && isClassProp ) + node->AddChildLast(ParseToken(ttPrivate)); + else if( t.type == ttProtected && isClassProp ) + node->AddChildLast(ParseToken(ttProtected)); + + // Parse data type + node->AddChildLast(ParseType(true, false, !isClassProp)); + if( isSyntaxError ) return node; + + for(;;) + { + // Parse identifier + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + if( isClassProp || isGlobalVar ) + { + // Only superficially parse the initialization info for the class property + GetToken(&t); + RewindTo(&t); + if( t.type == ttAssignment || t.type == ttOpenParanthesis ) + { + node->AddChildLast(SuperficiallyParseVarInit()); + if( isSyntaxError ) return node; + } + } + else + { + // If next token is assignment, parse expression + GetToken(&t); + if( t.type == ttOpenParanthesis ) + { + RewindTo(&t); + node->AddChildLast(ParseArgList()); + if( isSyntaxError ) return node; + } + else if( t.type == ttAssignment ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttStartStatementBlock ) + { + node->AddChildLast(ParseInitList()); + if( isSyntaxError ) return node; + } + else + { + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + } + else + RewindTo(&t); + } + + // continue if list separator, else terminate with end statement + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + + return node; + } + else + { + Error(ExpectedTokens(",", ";"), &t); + Error(InsteadFound(t), &t); + return node; + } + } + UNREACHABLE_RETURN; +} + +// BNF: STATEMENT ::= (IF | FOR | WHILE | RETURN | STATBLOCK | BREAK | CONTINUE | DOWHILE | SWITCH | EXPRSTAT) +asCScriptNode *asCParser::ParseStatement() +{ + sToken t1; + + GetToken(&t1); + RewindTo(&t1); + + if( t1.type == ttIf ) + return ParseIf(); + else if( t1.type == ttFor ) + return ParseFor(); + else if( t1.type == ttWhile ) + return ParseWhile(); + else if( t1.type == ttReturn ) + return ParseReturn(); + else if( t1.type == ttStartStatementBlock ) + return ParseStatementBlock(); + else if( t1.type == ttBreak ) + return ParseBreak(); + else if( t1.type == ttContinue ) + return ParseContinue(); + else if( t1.type == ttDo ) + return ParseDoWhile(); + else if( t1.type == ttSwitch ) + return ParseSwitch(); + else + { + if( IsVarDecl() ) + { + Error(TXT_UNEXPECTED_VAR_DECL, &t1); + return 0; + } + return ParseExpressionStatement(); + } +} + +// BNF: EXPRSTAT ::= [ASSIGN] ';' +asCScriptNode *asCParser::ParseExpressionStatement() +{ + asCScriptNode *node = CreateNode(snExpressionStatement); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + + return node; + } + + RewindTo(&t); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: SWITCH ::= 'switch' '(' ASSIGN ')' '{' {CASE} '}' +asCScriptNode *asCParser::ParseSwitch() +{ + asCScriptNode *node = CreateNode(snSwitch); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttSwitch ) + { + Error(ExpectedToken("switch"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); + return node; + } + + GetToken(&t); + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); + return node; + } + + while( !isSyntaxError ) + { + GetToken(&t); + + if( t.type == ttEndStatementBlock ) + break; + + RewindTo(&t); + + if( t.type != ttCase && t.type != ttDefault ) + { + const char *tokens[] = {"case", "default"}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseCase()); + if( isSyntaxError ) return node; + } + + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); + return node; + } + + return node; +} + +// BNF: CASE ::= (('case' EXPR) | 'default') ':' {STATEMENT} +asCScriptNode *asCParser::ParseCase() +{ + asCScriptNode *node = CreateNode(snCase); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttCase && t.type != ttDefault ) + { + Error(ExpectedTokens("case", "default"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + if(t.type == ttCase) + { + node->AddChildLast(ParseExpression()); + } + + GetToken(&t); + if( t.type != ttColon ) + { + Error(ExpectedToken(":"), &t); + Error(InsteadFound(t), &t); + return node; + } + + // Parse statements until we find either of }, case, default, and break + GetToken(&t); + RewindTo(&t); + while( t.type != ttCase && + t.type != ttDefault && + t.type != ttEndStatementBlock && + t.type != ttBreak ) + { + if( IsVarDecl() ) + // Variable declarations are not allowed, but we parse it anyway to give a good error message + node->AddChildLast(ParseDeclaration()); + else + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + RewindTo(&t); + } + + // If the case was ended with a break statement, add it to the node + if( t.type == ttBreak ) + node->AddChildLast(ParseBreak()); + + return node; +} + +// BNF: IF ::= 'if' '(' ASSIGN ')' STATEMENT ['else' STATEMENT] +asCScriptNode *asCParser::ParseIf() +{ + asCScriptNode *node = CreateNode(snIf); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttIf ) + { + Error(ExpectedToken("if"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttElse ) + { + // No else statement return already + RewindTo(&t); + return node; + } + + node->AddChildLast(ParseStatement()); + + return node; +} + +// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN {',' ASSIGN}] ')' STATEMENT +asCScriptNode *asCParser::ParseFor() +{ + asCScriptNode *node = CreateNode(snFor); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttFor ) + { + Error(ExpectedToken("for"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); + return node; + } + + if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration()); + else + node->AddChildLast(ParseExpressionStatement()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseExpressionStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + RewindTo(&t); + + // Parse N increment statements separated by , + for(;;) + { + asCScriptNode *n = CreateNode(snExpressionStatement); + if( n == 0 ) return 0; + node->AddChildLast(n); + n->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttCloseParanthesis ) + break; + else + { + const char *tokens[] = {",", ")"}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + return node; + } + } + } + + node->AddChildLast(ParseStatement()); + + return node; +} + +// BNF: WHILE ::= 'while' '(' ASSIGN ')' STATEMENT +asCScriptNode *asCParser::ParseWhile() +{ + asCScriptNode *node = CreateNode(snWhile); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttWhile ) + { + Error(ExpectedToken("while"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseStatement()); + + return node; +} + +// BNF: DOWHILE ::= 'do' STATEMENT 'while' '(' ASSIGN ')' ';' +asCScriptNode *asCParser::ParseDoWhile() +{ + asCScriptNode *node = CreateNode(snDoWhile); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttDo ) + { + Error(ExpectedToken("do"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttWhile ) + { + Error(ExpectedToken("while"), &t); + Error(InsteadFound(t), &t); + return node; + } + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); + return node; + } + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + return node; + } + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: RETURN ::= 'return' [ASSIGN] ';' +asCScriptNode *asCParser::ParseReturn() +{ + asCScriptNode *node = CreateNode(snReturn); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttReturn ) + { + Error(ExpectedToken("return"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + return node; + } + + RewindTo(&t); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: BREAK ::= 'break' ';' +asCScriptNode *asCParser::ParseBreak() +{ + asCScriptNode *node = CreateNode(snBreak); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttBreak ) + { + Error(ExpectedToken("break"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// BNF: CONTINUE ::= 'continue' ';' +asCScriptNode *asCParser::ParseContinue() +{ + asCScriptNode *node = CreateNode(snContinue); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + if( t.type != ttContinue ) + { + Error(ExpectedToken("continue"), &t); + Error(InsteadFound(t), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +// TODO: typedef: Typedefs should accept complex types as well +// BNF: TYPEDEF ::= 'typedef' PRIMTYPE IDENTIFIER ';' +asCScriptNode *asCParser::ParseTypedef() +{ + // Create the typedef node + asCScriptNode *node = CreateNode(snTypedef); + if( node == 0 ) return 0; + + sToken token; + + GetToken(&token); + if( token.type != ttTypedef) + { + Error(ExpectedToken(asCTokenizer::GetDefinition(ttTypedef)), &token); + Error(InsteadFound(token), &token); + return node; + } + + node->SetToken(&token); + node->UpdateSourcePos(token.pos, token.length); + + // Parse the base type + GetToken(&token); + RewindTo(&token); + + // Make sure it is a primitive type (except ttVoid) + if( !IsRealType(token.type) || token.type == ttVoid ) + { + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(token.type)); + Error(str, &token); + return node; + } + + node->AddChildLast(ParseRealType()); + node->AddChildLast(ParseIdentifier()); + + // Check for the end of the typedef + GetToken(&token); + if( token.type != ttEndStatement ) + { + RewindTo(&token); + Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(InsteadFound(token), &token); + } + + return node; +} + +void asCParser::ParseMethodOverrideBehaviors(asCScriptNode *funcNode) +{ + sToken t1; + + for(;;) + { + GetToken(&t1); + RewindTo(&t1); + + if( IdentifierIs(t1, FINAL_TOKEN) || IdentifierIs(t1, OVERRIDE_TOKEN) ) + funcNode->AddChildLast(ParseIdentifier()); + else + break; + } +} +#endif + +END_AS_NAMESPACE + + diff --git a/3rdparty/angelscript/src/as_restore.cpp b/3rdparty/angelscript/src/as_restore.cpp new file mode 100644 index 0000000..4c1682f --- /dev/null +++ b/3rdparty/angelscript/src/as_restore.cpp @@ -0,0 +1,5300 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_restore.cpp +// +// Functions for saving and restoring module bytecode +// asCRestore was originally written by Dennis Bollyn, dennis@gyrbo.be + +#include "as_config.h" +#include "as_restore.h" +#include "as_bytecode.h" +#include "as_scriptobject.h" +#include "as_texts.h" +#include "as_debug.h" + +BEGIN_AS_NAMESPACE + +// Macros for doing endianess agnostic bitmask serialization +#define SAVE_TO_BIT(dst, val, bit) ((dst) |= ((val) << (bit))) +#define LOAD_FROM_BIT(dst, val, bit) ((dst) = ((val) >> (bit)) & 1) + +asCReader::asCReader(asCModule* _module, asIBinaryStream* _stream, asCScriptEngine* _engine) + : module(_module), stream(_stream), engine(_engine) +{ + error = false; + bytesRead = 0; +} + +void asCReader::ReadData(void *data, asUINT size) +{ + asASSERT(size == 1 || size == 2 || size == 4 || size == 8); +#if defined(AS_BIG_ENDIAN) + for( asUINT n = 0; n < size; n++ ) + stream->Read(((asBYTE*)data)+n, 1); +#else + for( int n = size-1; n >= 0; n-- ) + stream->Read(((asBYTE*)data)+n, 1); +#endif + bytesRead += size; +} + +int asCReader::Read(bool *wasDebugInfoStripped) +{ + TimeIt("asCReader::Read"); + + // Before starting the load, make sure that + // any existing resources have been freed + module->InternalReset(); + + // Call the inner method to do the actual loading + int r = ReadInner(); + if( r < 0 ) + { + // Something went wrong while loading the bytecode, so we need + // to clean-up whatever has been created during the process. + + // Make sure none of the loaded functions attempt to release + // references that have not yet been increased + asUINT i; + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( !dontTranslate.MoveTo(0, module->scriptFunctions[i]) ) + if( module->scriptFunctions[i]->scriptData ) + module->scriptFunctions[i]->scriptData->byteCode.SetLength(0); + + asCSymbolTable::iterator it = module->scriptGlobals.List(); + for( ; it; it++ ) + if( (*it)->GetInitFunc() ) + if( (*it)->GetInitFunc()->scriptData ) + (*it)->GetInitFunc()->scriptData->byteCode.SetLength(0); + + module->InternalReset(); + } + else + { + // Init system functions properly + engine->PrepareEngine(); + + // Initialize the global variables (unless requested not to) + if( engine->ep.initGlobalVarsAfterBuild ) + r = module->ResetGlobalVars(0); + + if( wasDebugInfoStripped ) + *wasDebugInfoStripped = noDebugInfo; + } + + return r; +} + +int asCReader::Error(const char *msg) +{ + // Don't write if it has already been reported an error earlier + if( !error ) + { + asCString str; + str.Format(msg, bytesRead); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + error = true; + } + + return asERROR; +} + +int asCReader::ReadInner() +{ + TimeIt("asCReader::ReadInner"); + + // This function will load each entity one by one from the stream. + // If any error occurs, it will return to the caller who is + // responsible for cleaning up the partially loaded entities. + + engine->deferValidationOfTemplateTypes = true; + + unsigned long i, count; + asCScriptFunction* func; + + ReadData(&noDebugInfo, 1); + + // Read enums + count = ReadEncodedUInt(); + module->enumTypes.Allocate(count, false); + for( i = 0; i < count && !error; i++ ) + { + asCEnumType *et = asNEW(asCEnumType)(engine); + if( et == 0 ) + { + error = true; + return asOUT_OF_MEMORY; + } + + ReadTypeDeclaration(et, 1); + + // If the type is shared then we should use the original if it exists + bool sharedExists = false; + if( et->IsShared() ) + { + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) + { + asCTypeInfo *t = engine->sharedScriptTypes[n]; + if( t && + t->IsShared() && + t->name == et->name && + t->nameSpace == et->nameSpace && + (t->flags & asOBJ_ENUM) ) + { + asDELETE(et, asCEnumType); + et = CastToEnumType(t); + sharedExists = true; + break; + } + } + } + + if( sharedExists ) + { + existingShared.Insert(et, true); + et->AddRefInternal(); + } + else + { + if( et->IsShared() ) + { + engine->sharedScriptTypes.PushLast(et); + et->AddRefInternal(); + } + + // Set this module as the owner + et->module = module; + } + module->enumTypes.PushLast(et); + ReadTypeDeclaration(et, 2); + } + + if( error ) return asERROR; + + // classTypes[] + // First restore the structure names, then the properties + count = ReadEncodedUInt(); + module->classTypes.Allocate(count, false); + for( i = 0; i < count && !error; ++i ) + { + asCObjectType *ot = asNEW(asCObjectType)(engine); + if( ot == 0 ) + { + error = true; + return asOUT_OF_MEMORY; + } + + ReadTypeDeclaration(ot, 1); + + // If the type is shared, then we should use the original if it exists + bool sharedExists = false; + if( ot->IsShared() ) + { + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) + { + asCTypeInfo *ti = engine->sharedScriptTypes[n]; + asCObjectType *t = CastToObjectType(ti); + if( t && + t->IsShared() && + t->name == ot->name && + t->nameSpace == ot->nameSpace && + t->IsInterface() == ot->IsInterface() ) + { + asDELETE(ot, asCObjectType); + ot = CastToObjectType(t); + sharedExists = true; + break; + } + } + } + + if( sharedExists ) + { + existingShared.Insert(ot, true); + ot->AddRefInternal(); + } + else + { + if( ot->IsShared() ) + { + engine->sharedScriptTypes.PushLast(ot); + ot->AddRefInternal(); + } + + // Set this module as the owner + ot->module = module; + } + module->classTypes.PushLast(ot); + } + + if( error ) return asERROR; + + // Read func defs + count = ReadEncodedUInt(); + module->funcDefs.Allocate(count, false); + for( i = 0; i < count && !error; i++ ) + { + bool isNew; + asCScriptFunction *funcDef = ReadFunction(isNew, false, true); + if(funcDef) + { + funcDef->module = module; + + asCFuncdefType *fdt = funcDef->funcdefType; + fdt->module = module; + + module->funcDefs.PushLast(fdt); + engine->funcDefs.PushLast(fdt); + + // TODO: clean up: This is also done by the builder. It should probably be moved to a method in the module + // Check if there is another identical funcdef from another module and if so reuse that instead + if(funcDef->isShared) + { + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + { + asCFuncdefType *f2 = engine->funcDefs[n]; + if( f2 == 0 || fdt == f2 ) + continue; + + if( !f2->funcdef->isShared ) + continue; + + if( f2->name == fdt->name && + f2->nameSpace == fdt->nameSpace && + f2->parentClass == fdt->parentClass && + f2->funcdef->IsSignatureExceptNameEqual(funcDef) ) + { + // Replace our funcdef for the existing one + module->funcDefs[module->funcDefs.IndexOf(fdt)] = f2; + f2->AddRefInternal(); + + engine->funcDefs.RemoveValue(fdt); + + savedFunctions[savedFunctions.IndexOf(funcDef)] = f2->funcdef; + + if (fdt->parentClass) + { + // The real funcdef should already be in the object + asASSERT(fdt->parentClass->childFuncDefs.IndexOf(f2) >= 0); + + fdt->parentClass = 0; + } + + fdt->ReleaseInternal(); + funcDef = 0; + break; + } + } + } + + // Add the funcdef to the parentClass if this is a child funcdef + if (funcDef && fdt->parentClass) + fdt->parentClass->childFuncDefs.PushLast(fdt); + } + else + Error(TXT_INVALID_BYTECODE_d); + } + + // Read interface methods + for( i = 0; i < module->classTypes.GetLength() && !error; i++ ) + { + if( module->classTypes[i]->IsInterface() ) + ReadTypeDeclaration(module->classTypes[i], 2); + } + + // Read class methods and behaviours + for( i = 0; i < module->classTypes.GetLength() && !error; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + ReadTypeDeclaration(module->classTypes[i], 2); + } + + // Read class properties + for( i = 0; i < module->classTypes.GetLength() && !error; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + ReadTypeDeclaration(module->classTypes[i], 3); + } + + if( error ) return asERROR; + + // Read typedefs + count = ReadEncodedUInt(); + module->typeDefs.Allocate(count, false); + for( i = 0; i < count && !error; i++ ) + { + asCTypedefType *td = asNEW(asCTypedefType)(engine); + if( td == 0 ) + { + error = true; + return asOUT_OF_MEMORY; + } + + ReadTypeDeclaration(td, 1); + td->module = module; + module->typeDefs.PushLast(td); + ReadTypeDeclaration(td, 2); + } + + if( error ) return asERROR; + + // scriptGlobals[] + count = ReadEncodedUInt(); + if( count && engine->ep.disallowGlobalVars ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_GLOBAL_VARS_NOT_ALLOWED); + Error(TXT_INVALID_BYTECODE_d); + } + module->scriptGlobals.Allocate(count, false); + for( i = 0; i < count && !error; ++i ) + { + ReadGlobalProperty(); + } + + // scriptFunctions[] + count = ReadEncodedUInt(); + for( i = 0; i < count && !error; ++i ) + { + size_t len = module->scriptFunctions.GetLength(); + bool isNew; + func = ReadFunction(isNew); + if( func == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + break; + } + + // Is the function shared and was it created now? + if( func->isShared && len != module->scriptFunctions.GetLength() ) + { + // If the function already existed in another module, then + // we need to replace it with previously existing one + for( asUINT n = 0; n < engine->scriptFunctions.GetLength() && !error; n++ ) + { + asCScriptFunction *realFunc = engine->scriptFunctions[n]; + if( realFunc && + realFunc != func && + realFunc->IsShared() && + realFunc->IsSignatureEqual(func) ) + { + // Replace the recently created function with the pre-existing function + module->scriptFunctions[module->scriptFunctions.GetLength()-1] = realFunc; + realFunc->AddRefInternal(); + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + engine->RemoveScriptFunction(func); + + // Insert the function in the dontTranslate array + dontTranslate.Insert(realFunc, true); + + // Release the function, but make sure nothing else is released + func->id = 0; + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + break; + } + } + } + } + + // globalFunctions[] + count = ReadEncodedUInt(); + for( i = 0; i < count && !error; ++i ) + { + bool isNew; + func = ReadFunction(isNew, false, false); + if( func ) + { + // All the global functions were already loaded while loading the scriptFunctions, here + // we're just re-reading the references to know which goes into the globalFunctions array + asASSERT( !isNew ); + + module->globalFunctions.Put(func); + } + else + Error(TXT_INVALID_BYTECODE_d); + } + + if( error ) return asERROR; + + // bindInformations[] + count = ReadEncodedUInt(); + module->bindInformations.Allocate(count, false); + for( i = 0; i < count && !error; ++i ) + { + sBindInfo *info = asNEW(sBindInfo); + if( info == 0 ) + { + error = true; + return asOUT_OF_MEMORY; + } + + bool isNew; + info->importedFunctionSignature = ReadFunction(isNew, false, false); + if( info->importedFunctionSignature == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + break; + } + + if( engine->freeImportedFunctionIdxs.GetLength() ) + { + int id = engine->freeImportedFunctionIdxs.PopLast(); + info->importedFunctionSignature->id = int(FUNC_IMPORTED + id); + engine->importedFunctions[id] = info; + } + else + { + info->importedFunctionSignature->id = int(FUNC_IMPORTED + engine->importedFunctions.GetLength()); + engine->importedFunctions.PushLast(info); + } + ReadString(&info->importFromModule); + info->boundFunctionId = -1; + module->bindInformations.PushLast(info); + } + + if( error ) return asERROR; + + // usedTypes[] + count = ReadEncodedUInt(); + usedTypes.Allocate(count, false); + for( i = 0; i < count && !error; ++i ) + { + asCTypeInfo *ti = ReadTypeInfo(); + usedTypes.PushLast(ti); + } + + // usedTypeIds[] + if( !error ) + ReadUsedTypeIds(); + + // usedFunctions[] + if( !error ) + ReadUsedFunctions(); + + // usedGlobalProperties[] + if( !error ) + ReadUsedGlobalProps(); + + // usedStringConstants[] + if( !error ) + ReadUsedStringConstants(); + + // usedObjectProperties + if( !error ) + ReadUsedObjectProps(); + + // Validate the template types + if( !error ) + { + for( i = 0; i < usedTypes.GetLength() && !error; i++ ) + { + asCObjectType *ot = CastToObjectType(usedTypes[i]); + if( !ot || + !(ot->flags & asOBJ_TEMPLATE) || + !ot->beh.templateCallback ) + continue; + + bool dontGarbageCollect = false; + asCScriptFunction *callback = engine->scriptFunctions[ot->beh.templateCallback]; + if( !engine->CallGlobalFunctionRetBool(ot, &dontGarbageCollect, callback->sysFuncIntf, callback) ) + { + asCString sub = ot->templateSubTypes[0].Format(ot->nameSpace); + for( asUINT n = 1; n < ot->templateSubTypes.GetLength(); n++ ) + { + sub += ","; + sub += ot->templateSubTypes[n].Format(ot->nameSpace); + } + asCString str; + str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, ot->name.AddressOf(), sub.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + else + { + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + ot->flags &= ~asOBJ_GC; + } + } + } + engine->deferValidationOfTemplateTypes = false; + + if( error ) return asERROR; + + // Update the loaded bytecode to point to the correct types, property offsets, + // function ids, etc. This is basically a linking stage. + for( i = 0; i < module->scriptFunctions.GetLength() && !error; i++ ) + if( module->scriptFunctions[i]->funcType == asFUNC_SCRIPT ) + TranslateFunction(module->scriptFunctions[i]); + + asCSymbolTable::iterator globIt = module->scriptGlobals.List(); + while( globIt && !error ) + { + asCScriptFunction *initFunc = (*globIt)->GetInitFunc(); + if( initFunc ) + TranslateFunction(initFunc); + globIt++; + } + + if( error ) return asERROR; + + // Add references for all functions (except for the pre-existing shared code) + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( !dontTranslate.MoveTo(0, module->scriptFunctions[i]) ) + module->scriptFunctions[i]->AddReferences(); + + globIt = module->scriptGlobals.List(); + while( globIt ) + { + asCScriptFunction *initFunc = (*globIt)->GetInitFunc(); + if( initFunc ) + initFunc->AddReferences(); + globIt++; + } + return error ? asERROR : asSUCCESS; +} + +void asCReader::ReadUsedStringConstants() +{ + TimeIt("asCReader::ReadUsedStringConstants"); + + asCString str; + + asUINT count; + count = ReadEncodedUInt(); + usedStringConstants.Allocate(count, false); + for( asUINT i = 0; i < count; ++i ) + { + ReadString(&str); + usedStringConstants.PushLast(engine->AddConstantString(str.AddressOf(), str.GetLength())); + } +} + +void asCReader::ReadUsedFunctions() +{ + TimeIt("asCReader::ReadUsedFunctions"); + + asUINT count; + count = ReadEncodedUInt(); + usedFunctions.SetLength(count); + if( usedFunctions.GetLength() != count ) + { + // Out of memory + error = true; + return; + } + memset(usedFunctions.AddressOf(), 0, sizeof(asCScriptFunction *)*count); + + for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) + { + char c; + + // Read the data to be able to uniquely identify the function + + // Is the function from the module or the application? + ReadData(&c, 1); + + if( c == 'n' ) + { + // Null function pointer + usedFunctions[n] = 0; + } + else + { + asCScriptFunction func(engine, c == 'm' ? module : 0, asFUNC_DUMMY); + asCObjectType *parentClass = 0; + ReadFunctionSignature(&func, &parentClass); + if( error ) + { + func.funcType = asFUNC_DUMMY; + return; + } + + // Find the correct function + if( c == 'm' ) + { + if( func.funcType == asFUNC_IMPORTED ) + { + for( asUINT i = 0; i < module->bindInformations.GetLength(); i++ ) + { + asCScriptFunction *f = module->bindInformations[i]->importedFunctionSignature; + if( func.objectType != f->objectType || + func.funcType != f->funcType || + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.funcType == asFUNC_FUNCDEF ) + { + const asCArray &funcs = module->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]->funcdef; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) || funcs[i]->parentClass != parentClass ) + continue; + + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } + else + { + // TODO: optimize: Global functions should be searched for in module->globalFunctions + // TODO: optimize: funcdefs should be searched for in module->funcDefs + // TODO: optimize: object methods should be searched for directly in the object type + for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = module->scriptFunctions[i]; + if( func.objectType != f->objectType || + func.funcType != f->funcType || + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + } + else + { + if( func.funcType == asFUNC_FUNCDEF ) + { + // This is a funcdef (registered or shared) + const asCArray &funcs = engine->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]->funcdef; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) || funcs[i]->parentClass != parentClass ) + continue; + + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } + else if( func.name[0] == '$' ) + { + // This is a special function + + // Check for string factory + if( func.name == "$str" && engine->stringFactory && + func.IsSignatureExceptNameAndObjectTypeEqual(engine->stringFactory) ) + usedFunctions[n] = engine->stringFactory; + else if( func.name == "$beh0" && func.objectType ) + { + // This is a class constructor, so we can search directly in the object type's constructors + for( asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.name == "$fact" || func.name == "$beh3" ) + { + // This is a factory (or stub), so look for the function in the return type's factories + asCObjectType *objType = CastToObjectType(func.returnType.GetTypeInfo()); + if( objType ) + { + for( asUINT i = 0; i < objType->beh.factories.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.factories[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + } + else if( func.name == "$list" ) + { + // listFactory is used for both factory is global and returns a handle and constructor that is a method + asCObjectType *objType = func.objectType ? func.objectType : CastToObjectType(func.returnType.GetTypeInfo()); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh2" ) + { + // This is a destructor, so check the object type's destructor + asCObjectType *objType = func.objectType; + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.destruct]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh4" ) + { + // This is a list factory, so check the return type's list factory + asCObjectType *objType = CastToObjectType(func.returnType.GetTypeInfo()); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$dlgte" ) + { + // This is the delegate factory + asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY); + asASSERT( f && func.IsSignatureEqual(f) ); + usedFunctions[n] = f; + } + } + else if( func.objectType == 0 ) + { + // This is a global function + const asCArray &funcs = engine->registeredGlobalFuncs.GetIndexes(func.nameSpace, func.name); + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = engine->registeredGlobalFuncs.Get(funcs[i]); + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.objectType ) + { + // It is a class member, so we can search directly in the object type's members + // TODO: virtual function is different that implemented method + for( asUINT i = 0; i < func.objectType->methods.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->methods[i]]; + if( f == 0 || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + + if( usedFunctions[n] == 0 ) + { + // TODO: clean up: This part of the code should never happen. All functions should + // be found in the above logic. The only valid reason to come here + // is if the bytecode is wrong and the function doesn't exist anyway. + // This loop is kept temporarily until we can be certain all scenarios + // are covered. + for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[i]; + if( f == 0 || + func.objectType != f->objectType || + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + + // No function is expected to be found + asASSERT(usedFunctions[n] == 0); + } + } + + // Set the type to dummy so it won't try to release the id + func.funcType = asFUNC_DUMMY; + + if( usedFunctions[n] == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + } +} + +void asCReader::ReadFunctionSignature(asCScriptFunction *func, asCObjectType **parentClass) +{ + asUINT i, count; + asCDataType dt; + int num; + + ReadString(&func->name); + if( func->name == DELEGATE_FACTORY ) + { + // It's not necessary to read anymore, everything is known + asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY); + asASSERT( f ); + func->returnType = f->returnType; + func->parameterTypes = f->parameterTypes; + func->inOutFlags = f->inOutFlags; + func->funcType = f->funcType; + func->defaultArgs = f->defaultArgs; + func->nameSpace = f->nameSpace; + return; + } + + ReadDataType(&func->returnType); + + count = ReadEncodedUInt(); + if( count > 256 ) + { + // Too many arguments, must be something wrong in the file + Error(TXT_INVALID_BYTECODE_d); + return; + } + func->parameterTypes.Allocate(count, false); + for( i = 0; i < count; ++i ) + { + ReadDataType(&dt); + func->parameterTypes.PushLast(dt); + } + + func->inOutFlags.SetLength(func->parameterTypes.GetLength()); + if( func->inOutFlags.GetLength() != func->parameterTypes.GetLength() ) + { + // Out of memory + error = true; + return; + } + memset(func->inOutFlags.AddressOf(), 0, sizeof(asETypeModifiers)*func->inOutFlags.GetLength()); + count = ReadEncodedUInt(); + if( count > func->parameterTypes.GetLength() ) + { + // Cannot be more than the number of arguments + Error(TXT_INVALID_BYTECODE_d); + return; + } + for( i = 0; i < count; ++i ) + { + num = ReadEncodedUInt(); + func->inOutFlags[i] = static_cast(num); + } + + func->funcType = (asEFuncType)ReadEncodedUInt(); + + // Read the default args, from last to first + count = ReadEncodedUInt(); + if( count > func->parameterTypes.GetLength() ) + { + // Cannot be more than the number of arguments + Error(TXT_INVALID_BYTECODE_d); + return; + } + if( count ) + { + func->defaultArgs.SetLength(func->parameterTypes.GetLength()); + if( func->defaultArgs.GetLength() != func->parameterTypes.GetLength() ) + { + // Out of memory + error = true; + return; + } + memset(func->defaultArgs.AddressOf(), 0, sizeof(asCString*)*func->defaultArgs.GetLength()); + for( i = 0; i < count; i++ ) + { + asCString *str = asNEW(asCString); + if( str == 0 ) + { + // Out of memory + error = true; + return; + } + func->defaultArgs[func->defaultArgs.GetLength()-1-i] = str; + ReadString(str); + } + } + + func->objectType = CastToObjectType(ReadTypeInfo()); + if( func->objectType ) + { + func->objectType->AddRefInternal(); + + asBYTE b; + ReadData(&b, 1); + func->isReadOnly = (b & 1) ? true : false; + func->isPrivate = (b & 2) ? true : false; + func->isProtected = (b & 4) ? true : false; + func->nameSpace = engine->nameSpaces[0]; + } + else + { + if (func->funcType == asFUNC_FUNCDEF) + { + asBYTE b; + ReadData(&b, 1); + if (b == 'n') + { + asCString ns; + ReadString(&ns); + func->nameSpace = engine->AddNameSpace(ns.AddressOf()); + } + else if (b == 'o') + { + func->nameSpace = 0; + if (parentClass) + *parentClass = CastToObjectType(ReadTypeInfo()); + else + error = true; + } + else + error = true; + } + else + { + asCString ns; + ReadString(&ns); + func->nameSpace = engine->AddNameSpace(ns.AddressOf()); + } + } +} + +asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool addToEngine, bool addToGC) +{ + isNew = false; + if( error ) return 0; + + char c; + ReadData(&c, 1); + + if( c == '\0' ) + { + // There is no function, so return a null pointer + return 0; + } + + if( c == 'r' ) + { + // This is a reference to a previously saved function + asUINT index = ReadEncodedUInt(); + if( index < savedFunctions.GetLength() ) + return savedFunctions[index]; + else + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + } + + // Load the new function + isNew = true; + asCScriptFunction *func = asNEW(asCScriptFunction)(engine,0,asFUNC_DUMMY); + if( func == 0 ) + { + // Out of memory + error = true; + return 0; + } + savedFunctions.PushLast(func); + + int i, count; + asCDataType dt; + int num; + + asCObjectType *parentClass = 0; + ReadFunctionSignature(func, &parentClass); + if( error ) + { + func->DestroyHalfCreated(); + return 0; + } + + if( func->funcType == asFUNC_SCRIPT ) + { + func->AllocateScriptFunctionData(); + if( func->scriptData == 0 ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } + + if( addToGC && !addToModule ) + engine->gc.AddScriptObjectToGC(func, &engine->functionBehaviours); + + ReadByteCode(func); + + func->scriptData->variableSpace = ReadEncodedUInt(); + + count = ReadEncodedUInt(); + func->scriptData->objVariablePos.Allocate(count, false); + func->scriptData->objVariableTypes.Allocate(count, false); + for( i = 0; i < count; ++i ) + { + func->scriptData->objVariableTypes.PushLast(ReadTypeInfo()); + num = ReadEncodedUInt(); + func->scriptData->objVariablePos.PushLast(num); + + if( error ) + { + // No need to continue (the error has already been reported before) + func->DestroyHalfCreated(); + return 0; + } + } + if( count > 0 ) + func->scriptData->objVariablesOnHeap = ReadEncodedUInt(); + else + func->scriptData->objVariablesOnHeap = 0; + + int length = ReadEncodedUInt(); + func->scriptData->objVariableInfo.SetLength(length); + for( i = 0; i < length; ++i ) + { + func->scriptData->objVariableInfo[i].programPos = ReadEncodedUInt(); + func->scriptData->objVariableInfo[i].variableOffset = ReadEncodedUInt(); + asEObjVarInfoOption option = (asEObjVarInfoOption)ReadEncodedUInt(); + func->scriptData->objVariableInfo[i].option = option; + if (option != asOBJ_INIT && option != asOBJ_UNINIT && option != asBLOCK_BEGIN && option != asBLOCK_END) + { + error = true; + func->DestroyHalfCreated(); + return 0; + } + } + + if( !noDebugInfo ) + { + length = ReadEncodedUInt(); + func->scriptData->lineNumbers.SetLength(length); + if( int(func->scriptData->lineNumbers.GetLength()) != length ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } + for( i = 0; i < length; ++i ) + func->scriptData->lineNumbers[i] = ReadEncodedUInt(); + + // Read the array of script sections + length = ReadEncodedUInt(); + func->scriptData->sectionIdxs.SetLength(length); + if( int(func->scriptData->sectionIdxs.GetLength()) != length ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } + for( i = 0; i < length; ++i ) + { + if( (i & 1) == 0 ) + func->scriptData->sectionIdxs[i] = ReadEncodedUInt(); + else + { + asCString str; + ReadString(&str); + func->scriptData->sectionIdxs[i] = engine->GetScriptSectionNameIndex(str.AddressOf()); + } + } + } + + // Read the variable information + if( !noDebugInfo ) + { + length = ReadEncodedUInt(); + func->scriptData->variables.Allocate(length, false); + for( i = 0; i < length; i++ ) + { + asSScriptVariable *var = asNEW(asSScriptVariable); + if( var == 0 ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } + func->scriptData->variables.PushLast(var); + + var->declaredAtProgramPos = ReadEncodedUInt(); + var->stackOffset = ReadEncodedUInt(); + ReadString(&var->name); + ReadDataType(&var->type); + + if( error ) + { + // No need to continue (the error has already been reported before) + func->DestroyHalfCreated(); + return 0; + } + } + } + + char bits; + ReadData(&bits, 1); + func->isShared = bits & 1 ? true : false; + func->dontCleanUpOnException = bits & 2 ? true : false; + + // Read script section name + if( !noDebugInfo ) + { + asCString name; + ReadString(&name); + func->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(name.AddressOf()); + func->scriptData->declaredAt = ReadEncodedUInt(); + } + + // Read parameter names + if( !noDebugInfo ) + { + asUINT countParam = asUINT(ReadEncodedUInt64()); + if( countParam > func->parameterTypes.GetLength() ) + { + error = true; + func->DestroyHalfCreated(); + return 0; + } + func->parameterNames.SetLength(countParam); + for( asUINT n = 0; n < countParam; n++ ) + ReadString(&func->parameterNames[n]); + } + } + else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE ) + { + func->vfTableIdx = ReadEncodedUInt(); + } + else if( func->funcType == asFUNC_FUNCDEF ) + { + asBYTE bits; + ReadData(&bits, 1); + if( bits ) + func->isShared = true; + + // The asCFuncdefType constructor adds itself to the func->funcdefType member + asCFuncdefType *fdt = asNEW(asCFuncdefType)(engine, func); + fdt->parentClass = parentClass; + } + + if( addToModule ) + { + // The refCount is already 1 + module->scriptFunctions.PushLast(func); + func->module = module; + } + if( addToEngine ) + { + func->id = engine->GetNextScriptFunctionId(); + engine->AddScriptFunction(func); + } + if( func->objectType ) + func->ComputeSignatureId(); + + return func; +} + +void asCReader::ReadTypeDeclaration(asCTypeInfo *type, int phase) +{ + if( phase == 1 ) + { + // Read the initial attributes + ReadString(&type->name); + ReadData(&type->flags, 4); + type->size = ReadEncodedUInt(); + asCString ns; + ReadString(&ns); + type->nameSpace = engine->AddNameSpace(ns.AddressOf()); + + // Verify that the flags match the asCTypeInfo + if ((CastToEnumType(type) && !(type->flags & asOBJ_ENUM)) || + (CastToFuncdefType(type) && !(type->flags & asOBJ_FUNCDEF)) || + (CastToObjectType(type) && !(type->flags & (asOBJ_REF | asOBJ_VALUE)))) + { + error = true; + return; + } + + // Reset the size of script classes, since it will be recalculated as properties are added + if( (type->flags & asOBJ_SCRIPT_OBJECT) && type->size != 0 ) + type->size = sizeof(asCScriptObject); + + asCObjectType *ot = CastToObjectType(type); + if (ot) + { + // Use the default script class behaviours + ot->beh = engine->scriptTypeBehaviours.beh; + ot->beh.construct = 0; + ot->beh.factory = 0; + ot->beh.constructors.PopLast(); // These will be read from the file + ot->beh.factories.PopLast(); // These will be read from the file + engine->scriptFunctions[ot->beh.addref]->AddRefInternal(); + engine->scriptFunctions[ot->beh.release]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcEnumReferences]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcGetFlag]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcGetRefCount]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcSetFlag]->AddRefInternal(); + engine->scriptFunctions[ot->beh.copy]->AddRefInternal(); + // TODO: weak: Should not do this if the class has been declared with 'noweak' + engine->scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal(); + } + } + else if( phase == 2 ) + { + if( type->flags & asOBJ_ENUM ) + { + asCEnumType *t = CastToEnumType(type); + int count = ReadEncodedUInt(); + bool sharedExists = existingShared.MoveTo(0, type); + if( !sharedExists ) + { + t->enumValues.Allocate(count, false); + for( int n = 0; n < count; n++ ) + { + asSEnumValue *e = asNEW(asSEnumValue); + if( e == 0 ) + { + // Out of memory + error = true; + return; + } + ReadString(&e->name); + ReadData(&e->value, 4); // TODO: Should be encoded + t->enumValues.PushLast(e); + } + } + else + { + // Verify that the enum values exists in the original + asCString name; + int value; + for( int n = 0; n < count; n++ ) + { + ReadString(&name); + ReadData(&value, 4); // TODO: Should be encoded + bool found = false; + for( asUINT e = 0; e < t->enumValues.GetLength(); e++ ) + { + if( t->enumValues[e]->name == name && + t->enumValues[e]->value == value ) + { + found = true; + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + } + } + } + else if( type->flags & asOBJ_TYPEDEF ) + { + asCTypedefType *td = CastToTypedefType(type); + asASSERT(td); + eTokenType t = (eTokenType)ReadEncodedUInt(); + td->aliasForType = asCDataType::CreatePrimitive(t, false); + } + else + { + asCObjectType *ot = CastToObjectType(type); + asASSERT(ot); + + // If the type is shared and pre-existing, we should just + // validate that the loaded methods match the original + bool sharedExists = existingShared.MoveTo(0, type); + if( sharedExists ) + { + asCObjectType *dt = CastToObjectType(ReadTypeInfo()); + if( ot->derivedFrom != dt ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + } + else + { + ot->derivedFrom = CastToObjectType(ReadTypeInfo()); + if( ot->derivedFrom ) + ot->derivedFrom->AddRefInternal(); + } + + // interfaces[] / interfaceVFTOffsets[] + int size = ReadEncodedUInt(); + if( sharedExists ) + { + for( int n = 0; n < size; n++ ) + { + asCObjectType *intf = CastToObjectType(ReadTypeInfo()); + ReadEncodedUInt(); + + if( !type->Implements(intf) ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + } + } + else + { + ot->interfaces.Allocate(size, false); + ot->interfaceVFTOffsets.Allocate(size, false); + for( int n = 0; n < size; n++ ) + { + asCObjectType *intf = CastToObjectType(ReadTypeInfo()); + ot->interfaces.PushLast(intf); + + asUINT offset = ReadEncodedUInt(); + ot->interfaceVFTOffsets.PushLast(offset); + } + } + + // behaviours + if( !ot->IsInterface() && type->flags != asOBJ_TYPEDEF && type->flags != asOBJ_ENUM ) + { + bool isNew; + asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists); + if( sharedExists ) + { + // Find the real function in the object, and update the savedFunctions array + asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.destruct); + if( (realFunc == 0 && func == 0) || realFunc->IsSignatureEqual(func) ) + { + // If the function is not the last, then the substitution has already occurred before + if( func && savedFunctions[savedFunctions.GetLength()-1] == func ) + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + } + else + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + if( func ) + { + if( isNew ) + { + // Destroy the function without releasing any references + func->id = 0; + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + } + module->scriptFunctions.PushLast(realFunc); + realFunc->AddRefInternal(); + dontTranslate.Insert(realFunc, true); + } + } + else + { + if( func ) + { + ot->beh.destruct = func->id; + func->AddRefInternal(); + } + else + ot->beh.destruct = 0; + } + + size = ReadEncodedUInt(); + for( int n = 0; n < size; n++ ) + { + func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists); + if( func ) + { + if( sharedExists ) + { + // Find the real function in the object, and update the savedFunctions array + bool found = false; + for( asUINT f = 0; f < ot->beh.constructors.GetLength(); f++ ) + { + asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.constructors[f]); + if( realFunc->IsSignatureEqual(func) ) + { + // If the function is not the last, then the substitution has already occurred before + if( savedFunctions[savedFunctions.GetLength()-1] == func ) + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + found = true; + module->scriptFunctions.PushLast(realFunc); + realFunc->AddRefInternal(); + dontTranslate.Insert(realFunc, true); + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + if( isNew ) + { + // Destroy the function without releasing any references + func->id = 0; + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + } + } + else + { + ot->beh.constructors.PushLast(func->id); + func->AddRefInternal(); + + if( func->parameterTypes.GetLength() == 0 ) + ot->beh.construct = func->id; + } + } + else + { + Error(TXT_INVALID_BYTECODE_d); + } + + func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists); + if( func ) + { + if( sharedExists ) + { + // Find the real function in the object, and update the savedFunctions array + bool found = false; + for( asUINT f = 0; f < ot->beh.factories.GetLength(); f++ ) + { + asCScriptFunction *realFunc = engine->GetScriptFunction(ot->beh.factories[f]); + if( realFunc->IsSignatureEqual(func) ) + { + // If the function is not the last, then the substitution has already occurred before + if( savedFunctions[savedFunctions.GetLength()-1] == func ) + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + found = true; + module->scriptFunctions.PushLast(realFunc); + realFunc->AddRefInternal(); + dontTranslate.Insert(realFunc, true); + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + if( isNew ) + { + // Destroy the function without releasing any references + func->id = 0; + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + } + } + else + { + ot->beh.factories.PushLast(func->id); + func->AddRefInternal(); + + if( func->parameterTypes.GetLength() == 0 ) + ot->beh.factory = func->id; + } + } + else + { + Error(TXT_INVALID_BYTECODE_d); + } + } + } + + // methods[] + size = ReadEncodedUInt(); + int n; + for( n = 0; n < size; n++ ) + { + bool isNew; + asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists); + if( func ) + { + if( sharedExists ) + { + // Find the real function in the object, and update the savedFunctions array + bool found = false; + for( asUINT f = 0; f < ot->methods.GetLength(); f++ ) + { + asCScriptFunction *realFunc = engine->GetScriptFunction(ot->methods[f]); + if( realFunc->IsSignatureEqual(func) ) + { + // If the function is not the last, then the substitution has already occurred before + if( savedFunctions[savedFunctions.GetLength()-1] == func ) + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + found = true; + module->scriptFunctions.PushLast(realFunc); + realFunc->AddRefInternal(); + dontTranslate.Insert(realFunc, true); + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + if( isNew ) + { + // Destroy the function without releasing any references + func->id = 0; + if( func->scriptData ) + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + } + } + else + { + // If the method is the assignment operator we need to replace the default implementation + if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 && + func->parameterTypes[0].GetTypeInfo() == func->objectType && + (func->inOutFlags[0] & asTM_INREF) ) + { + engine->scriptFunctions[ot->beh.copy]->ReleaseInternal(); + ot->beh.copy = func->id; + func->AddRefInternal(); + } + + ot->methods.PushLast(func->id); + func->AddRefInternal(); + } + } + else + { + Error(TXT_INVALID_BYTECODE_d); + } + } + + // virtualFunctionTable[] + size = ReadEncodedUInt(); + for( n = 0; n < size; n++ ) + { + bool isNew; + asCScriptFunction *func = ReadFunction(isNew, !sharedExists, !sharedExists, !sharedExists); + if( func ) + { + if( sharedExists ) + { + // Find the real function in the object, and update the savedFunctions array + bool found = false; + for( asUINT f = 0; f < ot->virtualFunctionTable.GetLength(); f++ ) + { + asCScriptFunction *realFunc = ot->virtualFunctionTable[f]; + if( realFunc->IsSignatureEqual(func) ) + { + // If the function is not the last, then the substitution has already occurred before + if( savedFunctions[savedFunctions.GetLength()-1] == func ) + savedFunctions[savedFunctions.GetLength()-1] = realFunc; + found = true; + module->scriptFunctions.PushLast(realFunc); + realFunc->AddRefInternal(); + dontTranslate.Insert(realFunc, true); + break; + } + } + if( !found ) + { + asCString str; + str.Format(TXT_SHARED_s_DOESNT_MATCH_ORIGINAL, type->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + } + if( isNew ) + { + // Destroy the function without releasing any references + func->id = 0; + if( func->scriptData ) + func->scriptData->byteCode.SetLength(0); + func->ReleaseInternal(); + } + } + else + { + ot->virtualFunctionTable.PushLast(func); + func->AddRefInternal(); + } + } + else + { + Error(TXT_INVALID_BYTECODE_d); + } + } + } + } + else if( phase == 3 ) + { + asCObjectType *ot = CastToObjectType(type); + + // This is only done for object types + asASSERT(ot); + + // properties[] + asUINT size = ReadEncodedUInt(); + for( asUINT n = 0; n < size; n++ ) + ReadObjectProperty(ot); + } +} + +asWORD asCReader::ReadEncodedUInt16() +{ + asDWORD dw = ReadEncodedUInt(); + if( (dw>>16) != 0 && (dw>>16) != 0xFFFF ) + { + Error(TXT_INVALID_BYTECODE_d); + } + + return asWORD(dw & 0xFFFF); +} + +asUINT asCReader::ReadEncodedUInt() +{ + asQWORD qw = ReadEncodedUInt64(); + if( (qw>>32) != 0 && (qw>>32) != 0xFFFFFFFF ) + { + Error(TXT_INVALID_BYTECODE_d); + } + + return asUINT(qw & 0xFFFFFFFFu); +} + +asQWORD asCReader::ReadEncodedUInt64() +{ + asQWORD i = 0; + asBYTE b; + ReadData(&b, 1); + bool isNegative = ( b & 0x80 ) ? true : false; + b &= 0x7F; + + if( (b & 0x7F) == 0x7F ) + { + ReadData(&b, 1); i = asQWORD(b) << 56; + ReadData(&b, 1); i += asQWORD(b) << 48; + ReadData(&b, 1); i += asQWORD(b) << 40; + ReadData(&b, 1); i += asQWORD(b) << 32; + ReadData(&b, 1); i += asUINT(b) << 24; + ReadData(&b, 1); i += asUINT(b) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x7E) == 0x7E ) + { + i = asQWORD(b & 0x01) << 48; + ReadData(&b, 1); i += asQWORD(b) << 40; + ReadData(&b, 1); i += asQWORD(b) << 32; + ReadData(&b, 1); i += asUINT(b) << 24; + ReadData(&b, 1); i += asUINT(b) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x7C) == 0x7C ) + { + i = asQWORD(b & 0x03) << 40; + ReadData(&b, 1); i += asQWORD(b) << 32; + ReadData(&b, 1); i += asUINT(b) << 24; + ReadData(&b, 1); i += asUINT(b) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x78) == 0x78 ) + { + i = asQWORD(b & 0x07) << 32; + ReadData(&b, 1); i += asUINT(b) << 24; + ReadData(&b, 1); i += asUINT(b) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x70) == 0x70 ) + { + i = asUINT(b & 0x0F) << 24; + ReadData(&b, 1); i += asUINT(b) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x60) == 0x60 ) + { + i = asUINT(b & 0x1F) << 16; + ReadData(&b, 1); i += asUINT(b) << 8; + ReadData(&b, 1); i += b; + } + else if( (b & 0x40) == 0x40 ) + { + i = asUINT(b & 0x3F) << 8; + ReadData(&b, 1); i += b; + } + else + { + i = b; + } + if( isNegative ) + i = (asQWORD)(-asINT64(i)); + + return i; +} + +void asCReader::ReadString(asCString* str) +{ + asUINT len = ReadEncodedUInt(); + if( len & 1 ) + { + asUINT idx = len/2; + if( idx < savedStrings.GetLength() ) + *str = savedStrings[idx]; + else + Error(TXT_INVALID_BYTECODE_d); + } + else if( len > 0 ) + { + len /= 2; + str->SetLength(len); + stream->Read(str->AddressOf(), len); + + savedStrings.PushLast(*str); + } + else + str->SetLength(0); +} + +void asCReader::ReadGlobalProperty() +{ + asCString name; + asCDataType type; + + ReadString(&name); + + asCString ns; + ReadString(&ns); + asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); + + ReadDataType(&type); + + asCGlobalProperty *prop = module->AllocateGlobalProperty(name.AddressOf(), type, nameSpace); + + // Read the initialization function + bool isNew; + // Do not add the function to the GC at this time. It will + // only be added to the GC when the module releases the property + asCScriptFunction *func = ReadFunction(isNew, false, true, false); + if( func ) + { + // Make sure the function knows it is owned by the module + func->module = module; + + prop->SetInitFunc(func); + func->ReleaseInternal(); + } +} + +void asCReader::ReadObjectProperty(asCObjectType *ot) +{ + asCString name; + ReadString(&name); + asCDataType dt; + ReadDataType(&dt); + int flags = ReadEncodedUInt(); + bool isPrivate = (flags & 1) ? true : false; + bool isProtected = (flags & 2) ? true : false; + bool isInherited = (flags & 4) ? true : false; + + // TODO: shared: If the type is shared and pre-existing, we should just + // validate that the loaded methods match the original + if( !existingShared.MoveTo(0, ot) ) + ot->AddPropertyToClass(name, dt, isPrivate, isProtected, isInherited); +} + +void asCReader::ReadDataType(asCDataType *dt) +{ + // Check if this is a previously used type + asUINT idx = ReadEncodedUInt(); + if( idx != 0 ) + { + // Get the datatype from the cache + *dt = savedDataTypes[idx-1]; + return; + } + + // Read the type definition + eTokenType tokenType = (eTokenType)ReadEncodedUInt(); + + // Reserve a spot in the savedDataTypes + asUINT saveSlot = savedDataTypes.GetLength(); + savedDataTypes.PushLast(asCDataType()); + + // Read the datatype for the first time + asCTypeInfo *ti = 0; + if( tokenType == ttIdentifier ) + ti = ReadTypeInfo(); + + // Read type flags as a bitmask + // Endian-safe code + bool isObjectHandle, isHandleToConst, isReference, isReadOnly; + char b = 0; + ReadData(&b, 1); + LOAD_FROM_BIT(isObjectHandle, b, 0); + LOAD_FROM_BIT(isHandleToConst, b, 1); + LOAD_FROM_BIT(isReference, b, 2); + LOAD_FROM_BIT(isReadOnly, b, 3); + + if( tokenType == ttIdentifier ) + *dt = asCDataType::CreateType(ti, false); + else + *dt = asCDataType::CreatePrimitive(tokenType, false); + if( isObjectHandle ) + { + dt->MakeReadOnly(isHandleToConst ? true : false); + + // Here we must allow a scoped type to be a handle + // e.g. if the datatype is for a system function + dt->MakeHandle(true, true); + } + dt->MakeReadOnly(isReadOnly ? true : false); + dt->MakeReference(isReference ? true : false); + + // Update the previously saved slot + savedDataTypes[saveSlot] = *dt; +} + +asCTypeInfo* asCReader::ReadTypeInfo() +{ + asCTypeInfo *ot = 0; + char ch; + ReadData(&ch, 1); + if( ch == 'a' ) + { + // Read the name of the template type + asCString typeName, ns; + ReadString(&typeName); + ReadString(&ns); + asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); + + asCTypeInfo *tmp = engine->GetRegisteredType(typeName.AddressOf(), nameSpace); + asCObjectType *tmpl = CastToObjectType(tmp); + if( tmpl == 0 ) + { + asCString str; + str.Format(TXT_TEMPLATE_TYPE_s_DOESNT_EXIST, typeName.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + asUINT numSubTypes = ReadEncodedUInt(); + asCArray subTypes; + for( asUINT n = 0; n < numSubTypes; n++ ) + { + ReadData(&ch, 1); + if( ch == 's' ) + { + asCDataType dt; + ReadDataType(&dt); + subTypes.PushLast(dt); + } + else + { + eTokenType tokenType = (eTokenType)ReadEncodedUInt(); + asCDataType dt = asCDataType::CreatePrimitive(tokenType, false); + subTypes.PushLast(dt); + } + } + + // Return the actual template if the subtypes are the template's dummy types + if( tmpl->templateSubTypes == subTypes ) + ot = tmpl; + else + { + // Get the template instance type based on the loaded subtypes + ot = engine->GetTemplateInstanceType(tmpl, subTypes, module); + } + + if( ot == 0 ) + { + // Show all subtypes in error message + asCString sub = subTypes[0].Format(nameSpace); + for( asUINT n = 1; n < subTypes.GetLength(); n++ ) + { + sub += ","; + sub += subTypes[n].Format(nameSpace); + } + asCString str; + str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, typeName.AddressOf(), sub.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + } + else if( ch == 'l' ) + { + asCObjectType *st = CastToObjectType(ReadTypeInfo()); + if( st == 0 || st->beh.listFactory == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + ot = engine->GetListPatternType(st->beh.listFactory); + } + else if( ch == 's' ) + { + // Read the name of the template subtype + asCString typeName; + ReadString(&typeName); + + // Find the template subtype + ot = 0; + for( asUINT n = 0; n < engine->templateSubTypes.GetLength(); n++ ) + { + if( engine->templateSubTypes[n] && engine->templateSubTypes[n]->name == typeName ) + { + ot = engine->templateSubTypes[n]; + break; + } + } + + if( ot == 0 ) + { + asCString str; + str.Format(TXT_TEMPLATE_SUBTYPE_s_DOESNT_EXIST, typeName.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + } + else if( ch == 'o' ) + { + // Read the object type name + asCString typeName, ns; + ReadString(&typeName); + ReadString(&ns); + asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); + + if( typeName.GetLength() && typeName != "$obj" && typeName != "$func" ) + { + // Find the object type + ot = module->GetType(typeName.AddressOf(), nameSpace); + if (!ot) + ot = engine->GetRegisteredType(typeName.AddressOf(), nameSpace); + + if( ot == 0 ) + { + asCString str; + str.Format(TXT_OBJECT_TYPE_s_DOESNT_EXIST, typeName.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + } + else if( typeName == "$obj" ) + { + ot = &engine->scriptTypeBehaviours; + } + else if( typeName == "$func" ) + { + ot = &engine->functionBehaviours; + } + else + asASSERT( false ); + } + else if (ch == 'c') + { + // Read the object type name + asCString typeName, ns; + ReadString(&typeName); + + // Read the parent class + asCObjectType *parentClass = CastToObjectType(ReadTypeInfo()); + if (parentClass == 0) + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + // Find the child type in the parentClass + for (asUINT n = 0; n < parentClass->childFuncDefs.GetLength(); n++) + { + if (parentClass->childFuncDefs[n]->name == typeName) + ot = parentClass->childFuncDefs[n]; + } + + if (ot == 0) + { + asCString str; + str.Format(TXT_OBJECT_TYPE_s_DOESNT_EXIST, typeName.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + } + else + { + // No object type + asASSERT( ch == '\0' || error ); + ot = 0; + } + + return ot; +} + +void asCReader::ReadByteCode(asCScriptFunction *func) +{ + asASSERT( func->scriptData ); + + // Read number of instructions + asUINT total, numInstructions; + total = numInstructions = ReadEncodedUInt(); + + // Reserve some space for the instructions + func->scriptData->byteCode.AllocateNoConstruct(numInstructions, false); + + asUINT pos = 0; + while( numInstructions ) + { + asBYTE b; + ReadData(&b, 1); + + // Allocate the space for the instruction + asUINT len = asBCTypeSize[asBCInfo[b].type]; + asUINT newSize = asUINT(func->scriptData->byteCode.GetLength()) + len; + if( func->scriptData->byteCode.GetCapacity() < newSize ) + { + // Determine the average size of the loaded instructions and re-estimate the final size + asUINT size = asUINT(float(newSize) / (total - numInstructions) * total) + 1; + func->scriptData->byteCode.AllocateNoConstruct(size, true); + } + if( !func->scriptData->byteCode.SetLengthNoConstruct(newSize) ) + { + // Out of memory + error = true; + return; + } + + asDWORD *bc = func->scriptData->byteCode.AddressOf() + pos; + pos += len; + + switch( asBCInfo[b].type ) + { + case asBCTYPE_NO_ARG: + { + *(asBYTE*)(bc) = b; + bc++; + } + break; + case asBCTYPE_W_ARG: + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + + bc++; + } + break; + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_W_DW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the word argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the dword argument + *bc++ = ReadEncodedUInt(); + } + break; + case asBCTYPE_DW_ARG: + { + *(asBYTE*)(bc) = b; + bc++; + + // Read the argument + *bc++ = ReadEncodedUInt(); + } + break; + case asBCTYPE_DW_DW_ARG: + { + *(asBYTE*)(bc) = b; + bc++; + + // Read the first argument + *bc++ = ReadEncodedUInt(); + + // Read the second argument + *bc++ = ReadEncodedUInt(); + } + break; + case asBCTYPE_wW_rW_rW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the first argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the second argument + w = ReadEncodedUInt16(); + *(asWORD*)bc = w; + + // Read the third argument + w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + + bc++; + } + break; + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + case asBCTYPE_wW_W_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the first argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the second argument + w = ReadEncodedUInt16(); + *(asWORD*)bc = w; + + bc++; + } + break; + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_W_DW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the first argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the second argument + w = ReadEncodedUInt16(); + *(asWORD*)bc = w; + bc++; + + // Read the third argument + asDWORD dw = ReadEncodedUInt(); + *bc++ = dw; + } + break; + case asBCTYPE_QW_ARG: + { + *(asBYTE*)(bc) = b; + bc++; + + // Read the argument + asQWORD qw = ReadEncodedUInt64(); + *(asQWORD*)bc = qw; + bc += 2; + } + break; + case asBCTYPE_QW_DW_ARG: + { + *(asBYTE*)(bc) = b; + bc++; + + // Read the first argument + asQWORD qw = ReadEncodedUInt64(); + *(asQWORD*)bc = qw; + bc += 2; + + // Read the second argument + asDWORD dw = ReadEncodedUInt(); + *bc++ = dw; + } + break; + case asBCTYPE_rW_QW_ARG: + case asBCTYPE_wW_QW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the first argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the argument + asQWORD qw = ReadEncodedUInt64(); + *(asQWORD*)bc = qw; + bc += 2; + } + break; + case asBCTYPE_rW_DW_DW_ARG: + { + *(asBYTE*)(bc) = b; + + // Read the 1st argument + asWORD w = ReadEncodedUInt16(); + *(((asWORD*)bc)+1) = w; + bc++; + + // Read the 2nd argument + *bc++ = ReadEncodedUInt(); + + // Read the 3rd argument + *bc++ = ReadEncodedUInt(); + } + break; + default: + { + // This should never happen + asASSERT(false); + + // Read the next 3 bytes + asDWORD c; asBYTE t; +#if defined(AS_BIG_ENDIAN) + c = b << 24; + ReadData(&t, 1); c += t << 16; + ReadData(&t, 1); c += t << 8; + ReadData(&t, 1); c += t; +#else + c = b; + ReadData(&t, 1); c += t << 8; + ReadData(&t, 1); c += t << 16; + ReadData(&t, 1); c += t << 24; +#endif + + *bc++ = c; + c = *(asBYTE*)&c; + + // Read the bc as is + for( int n = 1; n < asBCTypeSize[asBCInfo[c].type]; n++ ) + ReadData(&*bc++, 4); + } + } + + numInstructions--; + } + + // Correct the final size in case we over-estimated it + func->scriptData->byteCode.SetLengthNoConstruct(pos); +} + +void asCReader::ReadUsedTypeIds() +{ + TimeIt("asCReader::ReadUsedTypeIds"); + + asUINT count = ReadEncodedUInt(); + usedTypeIds.Allocate(count, false); + for( asUINT n = 0; n < count; n++ ) + { + asCDataType dt; + ReadDataType(&dt); + usedTypeIds.PushLast(engine->GetTypeIdFromDataType(dt)); + } +} + +void asCReader::ReadUsedGlobalProps() +{ + TimeIt("asCReader::ReadUsedGlobalProps"); + + int c = ReadEncodedUInt(); + + usedGlobalProperties.Allocate(c, false); + + for( int n = 0; n < c; n++ ) + { + asCString name, ns; + asCDataType type; + char moduleProp; + + ReadString(&name); + ReadString(&ns); + ReadDataType(&type); + ReadData(&moduleProp, 1); + + asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); + + // Find the real property + asCGlobalProperty *globProp = 0; + if( moduleProp ) + globProp = module->scriptGlobals.GetFirst(nameSpace, name); + else + globProp = engine->registeredGlobalProps.GetFirst(nameSpace, name); + + void *prop = 0; + if( globProp && globProp->type == type ) + prop = globProp->GetAddressOfValue(); + + usedGlobalProperties.PushLast(prop); + + if( prop == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + } + } +} + +void asCReader::ReadUsedObjectProps() +{ + TimeIt("asCReader::ReadUsedObjectProps"); + + asUINT c = ReadEncodedUInt(); + + usedObjectProperties.SetLength(c); + for( asUINT n = 0; n < c; n++ ) + { + asCObjectType *objType = CastToObjectType(ReadTypeInfo()); + if( objType == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + break; + } + + asCString name; + ReadString(&name); + + // Find the property offset + bool found = false; + for( asUINT p = 0; p < objType->properties.GetLength(); p++ ) + { + if( objType->properties[p]->name == name ) + { + usedObjectProperties[n].objType = objType; + usedObjectProperties[n].offset = objType->properties[p]->byteOffset; + found = true; + break; + } + } + + if( !found ) + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } +} + +short asCReader::FindObjectPropOffset(asWORD index) +{ + if( index >= usedObjectProperties.GetLength() ) + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + return (short)usedObjectProperties[index].offset; +} + +asCScriptFunction *asCReader::FindFunction(int idx) +{ + if( idx >= 0 && idx < (int)usedFunctions.GetLength() ) + return usedFunctions[idx]; + else + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } +} + +void asCReader::TranslateFunction(asCScriptFunction *func) +{ + // Skip this if the function is part of an pre-existing shared object + if( dontTranslate.MoveTo(0, func) ) return; + + asASSERT( func->scriptData ); + + // Pre-compute the size of each instruction in order to translate jump offsets + asUINT n; + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength(); + asCArray bcSizes(bcLength); + asCArray instructionNbrToPos(bcLength); + for( n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + asUINT size = asBCTypeSize[asBCInfo[c].type]; + if( size == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + bcSizes.PushLast(size); + instructionNbrToPos.PushLast(n); + n += size; + } + + asUINT bcNum = 0; + for( n = 0; n < bcLength; bcNum++ ) + { + int c = *(asBYTE*)&bc[n]; + if( c == asBC_REFCPY || + c == asBC_RefCpyV || + c == asBC_OBJTYPE ) + { + // Translate the index to the true object type + asPWORD *ot = (asPWORD*)&bc[n+1]; + *(asCObjectType**)ot = CastToObjectType(FindType(int(*ot))); + } + else if( c == asBC_TYPEID || + c == asBC_Cast ) + { + // Translate the index to the type id + int *tid = (int*)&bc[n+1]; + *tid = FindTypeId(*tid); + } + else if( c == asBC_ADDSi || + c == asBC_LoadThisR ) + { + // Translate the index to the type id + int *tid = (int*)&bc[n+1]; + *tid = FindTypeId(*tid); + + // Translate the prop index into the property offset + *(((short*)&bc[n])+1) = FindObjectPropOffset(*(((short*)&bc[n])+1)); + } + else if( c == asBC_LoadRObjR || + c == asBC_LoadVObjR ) + { + // Translate the index to the type id + int *tid = (int*)&bc[n+2]; + *tid = FindTypeId(*tid); + + asCObjectType *ot = engine->GetObjectTypeFromTypeId(*tid); + if( ot && (ot->flags & asOBJ_LIST_PATTERN) ) + { + // List patterns have a different way of adjusting the offsets + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + *(((short*)&bc[n])+2) = (short)listAdj->AdjustOffset(*(((short*)&bc[n])+2)); + } + else + { + // Translate the prop index into the property offset + *(((short*)&bc[n])+2) = FindObjectPropOffset(*(((short*)&bc[n])+2)); + } + } + else if( c == asBC_COPY ) + { + // Translate the index to the type id + int *tid = (int*)&bc[n+1]; + *tid = FindTypeId(*tid); + + // COPY is used to copy POD types that don't have the opAssign method. It is + // also used to copy references to scoped types during variable initializations. + // Update the number of dwords to copy as it may be different on the target platform + if( (*tid) & asTYPEID_OBJHANDLE ) + { + // It is the actual reference that is being copied, not the object itself + asBC_SWORDARG0(&bc[n]) = AS_PTR_SIZE; + } + else + { + asCDataType dt = engine->GetDataTypeFromTypeId(*tid); + if( !dt.IsValid() ) + { + Error(TXT_INVALID_BYTECODE_d); + } + else + asBC_SWORDARG0(&bc[n]) = (short)dt.GetSizeInMemoryDWords(); + } + } + else if( c == asBC_RET ) + { + // Determine the correct amount of DWORDs to pop + asWORD dw = (asWORD)func->GetSpaceNeededForArguments(); + if( func->DoesReturnOnStack() ) dw += AS_PTR_SIZE; + if( func->objectType ) dw += AS_PTR_SIZE; + asBC_WORDARG0(&bc[n]) = dw; + } + else if( c == asBC_CALL || + c == asBC_CALLINTF || + c == asBC_CALLSYS || + c == asBC_Thiscall1 ) + { + // Translate the index to the func id + int *fid = (int*)&bc[n+1]; + asCScriptFunction *f = FindFunction(*fid); + if( f ) + *fid = f->id; + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + else if( c == asBC_FuncPtr ) + { + // Translate the index to the func pointer + asPWORD *fid = (asPWORD*)&bc[n+1]; + *fid = (asPWORD)FindFunction(int(*fid)); + } + else if( c == asBC_ALLOC ) + { + // Translate the index to the true object type + asPWORD *arg = (asPWORD*)&bc[n+1]; + *(asCObjectType**)arg = CastToObjectType(FindType(int(*arg))); + + // The constructor function id must be translated, unless it is zero + int *fid = (int*)&bc[n+1+AS_PTR_SIZE]; + if( *fid != 0 ) + { + // Subtract 1 from the id, as it was incremented during the writing + asCScriptFunction *f = FindFunction(*fid-1); + if( f ) + *fid = f->id; + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + } + else if( c == asBC_STR ) + { + // Translate the index to the true string id + asWORD *arg = ((asWORD*)&bc[n])+1; + + if( *arg < usedStringConstants.GetLength() ) + *arg = (asWORD)usedStringConstants[*arg]; + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + else if( c == asBC_CALLBND ) + { + // Translate the function id + asUINT *fid = (asUINT*)&bc[n+1]; + if( *fid < module->bindInformations.GetLength() ) + { + sBindInfo *bi = module->bindInformations[*fid]; + if( bi ) + *fid = bi->importedFunctionSignature->id; + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + else if( c == asBC_PGA || + c == asBC_PshGPtr || + c == asBC_LDG || + c == asBC_PshG4 || + c == asBC_LdGRdR4 || + c == asBC_CpyGtoV4 || + c == asBC_CpyVtoG4 || + c == asBC_SetG4 ) + { + // Translate the global var index to pointer + asPWORD *index = (asPWORD*)&bc[n+1]; + if( asUINT(*index) < usedGlobalProperties.GetLength() ) + *(void**)index = usedGlobalProperties[asUINT(*index)]; + else + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + } + else if( c == asBC_JMP || + c == asBC_JZ || + c == asBC_JNZ || + c == asBC_JLowZ || + c == asBC_JLowNZ || + c == asBC_JS || + c == asBC_JNS || + c == asBC_JP || + c == asBC_JNP ) // The JMPP instruction doesn't need modification + { + // Get the offset + int offset = int(bc[n+1]); + + // Count the instruction sizes to the destination instruction + int size = 0; + if( offset >= 0 ) + // If moving ahead, then start from next instruction + for( asUINT num = bcNum+1; offset-- > 0; num++ ) + size += bcSizes[num]; + else + // If moving backwards, then start at current instruction + for( asUINT num = bcNum; offset++ < 0; num-- ) + size -= bcSizes[num]; + + // The size is dword offset + bc[n+1] = size; + } + else if( c == asBC_AllocMem ) + { + // The size of the allocated memory is only known after all the elements has been seen. + // This helper class will collect this information and adjust the size when the + // corresponding asBC_FREE is encountered + + // The adjuster also needs to know the list type so it can know the type of the elements + asCObjectType *ot = CastToObjectType(func->GetTypeInfoOfLocalVar(asBC_SWORDARG0(&bc[n]))); + listAdjusters.PushLast(asNEW(SListAdjuster)(this, &bc[n], ot)); + } + else if( c == asBC_FREE ) + { + // Translate the index to the true object type + asPWORD *pot = (asPWORD*)&bc[n+1]; + *(asCObjectType**)pot = CastToObjectType(FindType(int(*pot))); + + asCObjectType *ot = *(asCObjectType**)pot; + if( ot && (ot->flags & asOBJ_LIST_PATTERN) ) + { + if( listAdjusters.GetLength() == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + + // Finalize the adjustment of the list buffer that was initiated with asBC_AllocMem + SListAdjuster *list = listAdjusters.PopLast(); + list->AdjustAllocMem(); + asDELETE(list, SListAdjuster); + } + } + else if( c == asBC_SetListSize ) + { + // Adjust the offset in the list where the size is informed + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); + + // Inform the list adjuster how many values will be repeated + listAdj->SetRepeatCount(bc[n+2]); + } + else if( c == asBC_PshListElmnt ) + { + // Adjust the offset in the list where the size is informed + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); + } + else if( c == asBC_SetListType ) + { + // Adjust the offset in the list where the typeid is informed + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); + + // Translate the type id + bc[n+2] = FindTypeId(bc[n+2]); + + // Inform the list adjuster the type id of the next element + listAdj->SetNextType(bc[n+2]); + } + + n += asBCTypeSize[asBCInfo[c].type]; + } + + // Calculate the stack adjustments + CalculateAdjustmentByPos(func); + + // Adjust all variable positions in the bytecode + bc = func->scriptData->byteCode.AddressOf(); + for( n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + switch( asBCInfo[c].type ) + { + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_ARG: + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_wW_W_ARG: + case asBCTYPE_rW_QW_ARG: + case asBCTYPE_rW_W_DW_ARG: + case asBCTYPE_rW_DW_DW_ARG: + { + asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n])); + } + break; + + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_rW_ARG: + { + asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n])); + asBC_SWORDARG1(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG1(&bc[n])); + } + break; + + case asBCTYPE_wW_rW_rW_ARG: + { + asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n])); + asBC_SWORDARG1(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG1(&bc[n])); + asBC_SWORDARG2(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG2(&bc[n])); + } + break; + + default: + // The other types don't treat variables so won't be modified + break; + } + + n += asBCTypeSize[asBCInfo[c].type]; + } + + // Adjust the space needed for local variables + func->scriptData->variableSpace = AdjustStackPosition(func->scriptData->variableSpace); + + // Adjust the variable information. This will be used during the adjustment below + for( n = 0; n < func->scriptData->variables.GetLength(); n++ ) + { + func->scriptData->variables[n]->declaredAtProgramPos = instructionNbrToPos[func->scriptData->variables[n]->declaredAtProgramPos]; + func->scriptData->variables[n]->stackOffset = AdjustStackPosition(func->scriptData->variables[n]->stackOffset); + } + + // objVariablePos + for( n = 0; n < func->scriptData->objVariablePos.GetLength(); n++ ) + func->scriptData->objVariablePos[n] = AdjustStackPosition(func->scriptData->objVariablePos[n]); + + // Adjust the get offsets. This must be done in the second iteration because + // it relies on the function ids and variable position already being correct in the + // bytecodes that come after the GET instructions. + // TODO: optimize: Instead of doing a full extra loop. We can push the GET instructions + // on a stack, and then when a call instruction is found update all of them. + // This will also make the AdjustGetOffset() function quicker as it can + // receive the called function directly instead of having to search for it. + bc = func->scriptData->byteCode.AddressOf(); + for( n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + + if( c == asBC_GETREF || + c == asBC_GETOBJ || + c == asBC_GETOBJREF || + c == asBC_ChkNullS ) + { + asBC_WORDARG0(&bc[n]) = (asWORD)AdjustGetOffset(asBC_WORDARG0(&bc[n]), func, n); + } + + n += asBCTypeSize[asBCInfo[c].type]; + } + + for( n = 0; n < func->scriptData->objVariableInfo.GetLength(); n++ ) + { + // The program position must be adjusted as it is stored in number of instructions + func->scriptData->objVariableInfo[n].programPos = instructionNbrToPos[func->scriptData->objVariableInfo[n].programPos]; + func->scriptData->objVariableInfo[n].variableOffset = AdjustStackPosition(func->scriptData->objVariableInfo[n].variableOffset); + } + + // The program position (every even number) needs to be adjusted + // for the line numbers to be in number of dwords instead of number of instructions + for( n = 0; n < func->scriptData->lineNumbers.GetLength(); n += 2 ) + func->scriptData->lineNumbers[n] = instructionNbrToPos[func->scriptData->lineNumbers[n]]; + for( n = 0; n < func->scriptData->sectionIdxs.GetLength(); n += 2 ) + func->scriptData->sectionIdxs[n] = instructionNbrToPos[func->scriptData->sectionIdxs[n]]; + + CalculateStackNeeded(func); +} + +asCReader::SListAdjuster::SListAdjuster(asCReader *rd, asDWORD *bc, asCObjectType *listType) : + reader(rd), allocMemBC(bc), maxOffset(0), patternType(listType), repeatCount(0), lastOffset(-1), nextOffset(0), nextTypeId(-1) +{ + asASSERT( patternType && (patternType->flags & asOBJ_LIST_PATTERN) ); + + // Find the first expected value in the list + asSListPatternNode *node = patternType->engine->scriptFunctions[patternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern; + asASSERT( node && node->type == asLPT_START ); + patternNode = node->next; +} + +int asCReader::SListAdjuster::AdjustOffset(int offset) +{ + if( offset < lastOffset ) + { + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + // If it is the same offset being accessed again, just return the same adjusted value + if( lastOffset == offset ) + return lastAdjustedOffset; + + lastOffset = offset; + lastAdjustedOffset = maxOffset; + + // What is being expected at this position? + if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) + { + // Align the offset to 4 bytes boundary + if( maxOffset & 0x3 ) + { + maxOffset += 4 - (maxOffset & 0x3); + lastAdjustedOffset = maxOffset; + } + + // Don't move the patternNode yet because the caller must make a call to SetRepeatCount too + maxOffset += 4; + nextOffset = offset+1; + return lastAdjustedOffset; + } + else if( patternNode->type == asLPT_TYPE ) + { + const asCDataType &dt = reinterpret_cast(patternNode)->dataType; + if( dt.GetTokenType() == ttQuestion ) + { + if( nextTypeId != -1 ) + { + if( repeatCount > 0 ) + repeatCount--; + + asCDataType nextdt = patternType->engine->GetDataTypeFromTypeId(nextTypeId); + asUINT size; + if(nextdt.IsObjectHandle() || (nextdt.GetTypeInfo() && (nextdt.GetTypeInfo()->flags & asOBJ_REF)) ) + size = AS_PTR_SIZE*4; + else + size = nextdt.GetSizeInMemoryBytes(); + + // Align the offset to 4 bytes boundary + if( size >= 4 && (maxOffset & 0x3) ) + { + maxOffset += 4 - (maxOffset & 0x3); + lastAdjustedOffset = maxOffset; + } + + // Only move the patternNode if we're not expecting any more repeated entries + if( repeatCount == 0 ) + patternNode = patternNode->next; + + nextTypeId = -1; + + maxOffset += size; + nextOffset = offset+1; + return lastAdjustedOffset; + } + else + { + // Align the offset to 4 bytes boundary + if( maxOffset & 0x3 ) + { + maxOffset += 4 - (maxOffset & 0x3); + lastAdjustedOffset = maxOffset; + } + + // The first adjustment is for the typeId + maxOffset += 4; + nextOffset = offset+1; + return lastAdjustedOffset; + } + } + else + { + // Determine the size of the element + asUINT size; + if( dt.IsObjectHandle() || (dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_REF)) ) + size = AS_PTR_SIZE*4; + else + size = dt.GetSizeInMemoryBytes(); + + // If values are skipped, the offset needs to be incremented + while( nextOffset <= offset ) + { + if( repeatCount > 0 ) + repeatCount--; + + // Align the offset to 4 bytes boundary + if( size >= 4 && (maxOffset & 0x3) ) + maxOffset += 4 - (maxOffset & 0x3); + + lastAdjustedOffset = maxOffset; + nextOffset += 1; + maxOffset += size; + } + + // Only move the patternNode if we're not expecting any more repeated entries + if( repeatCount == 0 ) + patternNode = patternNode->next; + + nextOffset = offset+1; + return lastAdjustedOffset; + } + } + else if( patternNode->type == asLPT_START ) + { + if( repeatCount > 0 ) + repeatCount--; + SInfo info = {repeatCount, patternNode}; + stack.PushLast(info); + + repeatCount = 0; + patternNode = patternNode->next; + + lastOffset--; + return AdjustOffset(offset); + } + else if( patternNode->type == asLPT_END ) + { + if( stack.GetLength() == 0 ) + { + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + SInfo info = stack.PopLast(); + repeatCount = info.repeatCount; + if( repeatCount ) + patternNode = info.startNode; + else + patternNode = patternNode->next; + + lastOffset--; + return AdjustOffset(offset); + } + else + { + // Something is wrong with the pattern list declaration + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + UNREACHABLE_RETURN; +} + +void asCReader::SListAdjuster::SetRepeatCount(asUINT rc) +{ + // Make sure the list is expecting a repeat at this location + asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ); + + // Now move to the next patternNode + patternNode = patternNode->next; + + repeatCount = rc; +} + +void asCReader::SListAdjuster::AdjustAllocMem() +{ + allocMemBC[1] = maxOffset; +} + +void asCReader::SListAdjuster::SetNextType(int typeId) +{ + asASSERT( nextTypeId == -1 ); + + nextTypeId = typeId; +} + +void asCReader::CalculateStackNeeded(asCScriptFunction *func) +{ + asASSERT( func->scriptData ); + + int largestStackUsed = 0; + + // Clear the known stack size for each bytecode + asCArray stackSize; + stackSize.SetLength(func->scriptData->byteCode.GetLength()); + memset(&stackSize[0], -1, stackSize.GetLength()*4); + + // Add the first instruction to the list of unchecked code + // paths and set the stack size at that instruction to variableSpace + asCArray paths; + paths.PushLast(0); + stackSize[0] = func->scriptData->variableSpace; + + // Go through each of the code paths + for( asUINT p = 0; p < paths.GetLength(); ++p ) + { + asUINT pos = paths[p]; + int currStackSize = stackSize[pos]; + + asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[pos]; + if( bc == asBC_RET ) + continue; + + // Determine the change in stack size for this instruction + int stackInc = asBCInfo[bc].stackInc; + if( stackInc == 0xFFFF ) + { + // Determine the true delta from the instruction arguments + if( bc == asBC_CALL || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || + bc == asBC_CALLBND || + bc == asBC_ALLOC || + bc == asBC_CALLINTF || + bc == asBC_CallPtr ) + { + asCScriptFunction *called = GetCalledFunction(func, pos); + if( called ) + { + stackInc = -called->GetSpaceNeededForArguments(); + if( called->objectType ) + stackInc -= AS_PTR_SIZE; + if( called->DoesReturnOnStack() ) + stackInc -= AS_PTR_SIZE; + } + else + { + // It is an allocation for an object without a constructor + asASSERT( bc == asBC_ALLOC ); + stackInc = -AS_PTR_SIZE; + } + } + } + + currStackSize += stackInc; + asASSERT( currStackSize >= 0 ); + + if( currStackSize > largestStackUsed ) + largestStackUsed = currStackSize; + + if( bc == asBC_JMP ) + { + // Find the label that we should jump to + int offset = asBC_INTARG(&func->scriptData->byteCode[pos]); + pos += 2 + offset; + + // Add the destination as a new path + if( stackSize[pos] == -1 ) + { + stackSize[pos] = currStackSize; + paths.PushLast(pos); + } + else + asASSERT(stackSize[pos] == currStackSize); + continue; + } + else if( bc == asBC_JZ || bc == asBC_JNZ || + bc == asBC_JLowZ || bc == asBC_JLowNZ || + bc == asBC_JS || bc == asBC_JNS || + bc == asBC_JP || bc == asBC_JNP ) + { + // Find the label that is being jumped to + int offset = asBC_INTARG(&func->scriptData->byteCode[pos]); + + // Add both paths to the code paths + pos += 2; + if( stackSize[pos] == -1 ) + { + stackSize[pos] = currStackSize; + paths.PushLast(pos); + } + else + asASSERT(stackSize[pos] == currStackSize); + + pos += offset; + if( stackSize[pos] == -1 ) + { + stackSize[pos] = currStackSize; + paths.PushLast(pos); + } + else + asASSERT(stackSize[pos] == currStackSize); + + continue; + } + else if( bc == asBC_JMPP ) + { + pos++; + + // Add all subsequent JMP instructions to the path + while( *(asBYTE*)&func->scriptData->byteCode[pos] == asBC_JMP ) + { + if( stackSize[pos] == -1 ) + { + stackSize[pos] = currStackSize; + paths.PushLast(pos); + } + else + asASSERT(stackSize[pos] == currStackSize); + pos += 2; + } + continue; + } + else + { + // Add next instruction to the paths + pos += asBCTypeSize[asBCInfo[bc].type]; + if( stackSize[pos] == -1 ) + { + stackSize[pos] = currStackSize; + paths.PushLast(pos); + } + else + asASSERT(stackSize[pos] == currStackSize); + + continue; + } + } + + func->scriptData->stackNeeded = largestStackUsed; +} + +void asCReader::CalculateAdjustmentByPos(asCScriptFunction *func) +{ + // Adjust the offset of all negative variables (parameters) as + // all pointers have been stored as having a size of 1 dword + asUINT n; + asCArray adjustments; + asUINT offset = 0; + if( func->objectType ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += 1; + } + if( func->DoesReturnOnStack() ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += 1; + } + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( !func->parameterTypes[n].IsPrimitive() || + func->parameterTypes[n].IsReference() ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += 1; + } + else + { + asASSERT( func->parameterTypes[n].IsPrimitive() ); + offset += func->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + // Build look-up table with the adjustments for each stack position + adjustNegativeStackByPos.SetLength(offset); + memset(adjustNegativeStackByPos.AddressOf(), 0, adjustNegativeStackByPos.GetLength()*sizeof(int)); + for( n = 0; n < adjustments.GetLength(); n+=2 ) + { + int pos = adjustments[n]; + int adjust = adjustments[n+1]; + + for( asUINT i = pos+1; i < adjustNegativeStackByPos.GetLength(); i++ ) + adjustNegativeStackByPos[i] += adjust; + } + + // The bytecode has been stored as if all object variables take up only 1 dword. + // It is necessary to adjust to the size according to the current platform. + adjustments.SetLength(0); + int highestPos = 0; + for( n = 0; n < func->scriptData->objVariableTypes.GetLength(); n++ ) + { + // Determine the size the variable currently occupies on the stack + int size = AS_PTR_SIZE; + + // objVariableTypes is null if the type is a null pointer + if( func->scriptData->objVariableTypes[n] && + (func->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) && + n >= func->scriptData->objVariablesOnHeap ) + { + size = func->scriptData->objVariableTypes[n]->GetSize(); + if( size < 4 ) + size = 1; + else + size /= 4; + } + + // Check if type has a different size than stored + if( size > 1 ) + { + if( func->scriptData->objVariablePos[n] > highestPos ) + highestPos = func->scriptData->objVariablePos[n]; + + adjustments.PushLast(func->scriptData->objVariablePos[n]); + adjustments.PushLast(size-1); + } + } + + // Count position 0 too + adjustByPos.SetLength(highestPos+1); + memset(adjustByPos.AddressOf(), 0, adjustByPos.GetLength()*sizeof(int)); + + // Build look-up table with the adjustments for each stack position + for( n = 0; n < adjustments.GetLength(); n+=2 ) + { + int pos = adjustments[n]; + int adjust = adjustments[n+1]; + + for( asUINT i = pos; i < adjustByPos.GetLength(); i++ ) + adjustByPos[i] += adjust; + } +} + +int asCReader::AdjustStackPosition(int pos) +{ + if( pos >= (int)adjustByPos.GetLength() ) + { + // It can be higher for primitives allocated on top of highest object variable + if( adjustByPos.GetLength() ) + pos += (short)adjustByPos[adjustByPos.GetLength()-1]; + } + else if( pos >= 0 ) + pos += (short)adjustByPos[pos]; + else if( -pos >= (int)adjustNegativeStackByPos.GetLength() ) + Error(TXT_INVALID_BYTECODE_d); + else + pos += (short)adjustNegativeStackByPos[-pos]; + + return pos; +} + +asCScriptFunction *asCReader::GetCalledFunction(asCScriptFunction *func, asDWORD programPos) +{ + asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[programPos]; + + if( bc == asBC_CALL || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || + bc == asBC_CALLINTF ) + { + // Find the function from the function id in bytecode + int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos]); + return engine->scriptFunctions[funcId]; + } + else if( bc == asBC_ALLOC ) + { + // Find the function from the function id in the bytecode + int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos+AS_PTR_SIZE]); + return engine->scriptFunctions[funcId]; + } + else if( bc == asBC_CALLBND ) + { + // Find the function from the engine's bind array + int funcId = asBC_INTARG(&func->scriptData->byteCode[programPos]); + return engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature; + } + else if( bc == asBC_CallPtr ) + { + asUINT v; + int var = asBC_SWORDARG0(&func->scriptData->byteCode[programPos]); + + // Find the funcdef from the local variable + for( v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ ) + if( func->scriptData->objVariablePos[v] == var ) + return CastToFuncdefType(func->scriptData->objVariableTypes[v])->funcdef; + + // Look in parameters + int paramPos = 0; + if( func->objectType ) + paramPos -= AS_PTR_SIZE; + if( func->DoesReturnOnStack() ) + paramPos -= AS_PTR_SIZE; + for( v = 0; v < func->parameterTypes.GetLength(); v++ ) + { + if (var == paramPos) + { + if (func->parameterTypes[v].IsFuncdef()) + return CastToFuncdefType(func->parameterTypes[v].GetTypeInfo())->funcdef; + else + { + error = true; + return 0; + } + } + paramPos -= func->parameterTypes[v].GetSizeOnStackDWords(); + } + } + + return 0; +} + +int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos) +{ + // TODO: optimize: multiple instructions for the same function doesn't need to look for the function everytime + // the function can remember where it found the function and check if the programPos is still valid + + // Get offset 0 doesn't need adjustment + if( offset == 0 ) return 0; + + bool bcAlloc = false; + + // Find out which function that will be called + asCScriptFunction *calledFunc = 0; + int stackDelta = 0; + for( asUINT n = programPos; func->scriptData->byteCode.GetLength(); ) + { + asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; + if( bc == asBC_CALL || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || + bc == asBC_CALLINTF || + bc == asBC_ALLOC || + bc == asBC_CALLBND || + bc == asBC_CallPtr ) + { + // The alloc instruction allocates the object memory + // so it doesn't take the this pointer as input + if (bc == asBC_ALLOC) + bcAlloc = true; + + calledFunc = GetCalledFunction(func, n); + break; + } + else if( bc == asBC_REFCPY || + bc == asBC_COPY ) + { + // In this case we know there is only 1 pointer on the stack above + asASSERT( offset == 1 ); + return offset - (1 - AS_PTR_SIZE); + } + + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + + n += asBCTypeSize[asBCInfo[bc].type]; + } + + if( calledFunc == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return offset; + } + + // Count the number of pointers pushed on the stack above the + // current offset, and then adjust the offset accordingly + asUINT numPtrs = 0; + int currOffset = -stackDelta; + if( offset > currOffset && calledFunc->GetObjectType() && !bcAlloc ) + { + currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif + } + if( offset > currOffset && calledFunc->DoesReturnOnStack() ) + { + currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif + } + for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) + { + if( offset <= currOffset ) break; + + if( !calledFunc->parameterTypes[p].IsPrimitive() || + calledFunc->parameterTypes[p].IsReference() ) + { + currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif + + // The variable arg ? has an additiona 32bit integer with the typeid + if( calledFunc->parameterTypes[p].IsAnyType() ) + currOffset += 1; + } + else + { + // Enums or built-in primitives are passed by value + asASSERT( calledFunc->parameterTypes[p].IsPrimitive() ); + currOffset += calledFunc->parameterTypes[p].GetSizeOnStackDWords(); + } + } + + return offset - numPtrs * (1 - AS_PTR_SIZE); +} + +int asCReader::FindTypeId(int idx) +{ + if( idx >= 0 && idx < (int)usedTypeIds.GetLength() ) + return usedTypeIds[idx]; + else + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } +} + +asCTypeInfo *asCReader::FindType(int idx) +{ + if( idx < 0 || idx >= (int)usedTypes.GetLength() ) + { + Error(TXT_INVALID_BYTECODE_d); + return 0; + } + + return usedTypes[idx]; +} + +#ifndef AS_NO_COMPILER + +asCWriter::asCWriter(asCModule* _module, asIBinaryStream* _stream, asCScriptEngine* _engine, bool _stripDebug) + : module(_module), stream(_stream), engine(_engine), stripDebugInfo(_stripDebug) +{ +} + +void asCWriter::WriteData(const void *data, asUINT size) +{ + asASSERT(size == 1 || size == 2 || size == 4 || size == 8); +#if defined(AS_BIG_ENDIAN) + for( asUINT n = 0; n < size; n++ ) + stream->Write(((asBYTE*)data)+n, 1); +#else + for( int n = size-1; n >= 0; n-- ) + stream->Write(((asBYTE*)data)+n, 1); +#endif +} + +int asCWriter::Write() +{ + TimeIt("asCWriter::Write"); + + unsigned long i, count; + + // Store everything in the same order that the builder parses scripts + + // TODO: Should be possible to skip saving the enum values. They are usually not needed after the script is compiled anyway + // TODO: Should be possible to skip saving the typedefs. They are usually not needed after the script is compiled anyway + // TODO: Should be possible to skip saving constants. They are usually not needed after the script is compiled anyway + WriteData(&stripDebugInfo, sizeof(stripDebugInfo)); + + // Store enums + { + TimeIt("store enums"); + + count = (asUINT)module->enumTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteTypeDeclaration(module->enumTypes[i], 1); + WriteTypeDeclaration(module->enumTypes[i], 2); + } + } + + // Store type declarations first + { + TimeIt("type declarations"); + + count = (asUINT)module->classTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + // Store only the name of the class/interface types + WriteTypeDeclaration(module->classTypes[i], 1); + } + } + + // Store func defs + { + TimeIt("func defs"); + + count = (asUINT)module->funcDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + WriteFunction(module->funcDefs[i]->funcdef); + } + + // Now store all interface methods + { + TimeIt("interface methods"); + + count = (asUINT)module->classTypes.GetLength(); + for( i = 0; i < count; i++ ) + { + if( module->classTypes[i]->IsInterface() ) + WriteTypeDeclaration(module->classTypes[i], 2); + } + } + + // Then store the class methods and behaviours + { + TimeIt("class methods and behaviours"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteTypeDeclaration(module->classTypes[i], 2); + } + } + + // Then store the class properties + { + TimeIt("class properties"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteTypeDeclaration(module->classTypes[i], 3); + } + } + + // Store typedefs + { + TimeIt("type defs"); + + count = (asUINT)module->typeDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteTypeDeclaration(module->typeDefs[i], 1); + WriteTypeDeclaration(module->typeDefs[i], 2); + } + } + + // scriptGlobals[] + { + TimeIt("script globals"); + + count = (asUINT)module->scriptGlobals.GetSize(); + WriteEncodedInt64(count); + asCSymbolTable::iterator it = module->scriptGlobals.List(); + for( ; it; it++ ) + WriteGlobalProperty(*it); + } + + // scriptFunctions[] + { + TimeIt("scriptFunctions"); + + count = 0; + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( module->scriptFunctions[i]->objectType == 0 ) + count++; + WriteEncodedInt64(count); + for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) + if( module->scriptFunctions[i]->objectType == 0 ) + WriteFunction(module->scriptFunctions[i]); + } + + // globalFunctions[] + { + TimeIt("globalFunctions"); + + count = (int)module->globalFunctions.GetSize(); + asCSymbolTable::iterator funcIt = module->globalFunctions.List(); + WriteEncodedInt64(count); + while( funcIt ) + { + WriteFunction(*funcIt); + funcIt++; + } + } + + // bindInformations[] + { + TimeIt("bindInformations"); + + count = (asUINT)module->bindInformations.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + { + WriteFunction(module->bindInformations[i]->importedFunctionSignature); + WriteString(&module->bindInformations[i]->importFromModule); + } + } + + // usedTypes[] + { + TimeIt("usedTypes"); + + count = (asUINT)usedTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + WriteTypeInfo(usedTypes[i]); + } + + // usedTypeIds[] + WriteUsedTypeIds(); + + // usedFunctions[] + WriteUsedFunctions(); + + // usedGlobalProperties[] + WriteUsedGlobalProps(); + + // usedStringConstants[] + WriteUsedStringConstants(); + + // usedObjectProperties[] + WriteUsedObjectProps(); + + return asSUCCESS; +} + +int asCWriter::FindStringConstantIndex(int id) +{ + asSMapNode *cursor = 0; + if (stringIdToIndexMap.MoveTo(&cursor, id)) + return cursor->value; + + usedStringConstants.PushLast(id); + int index = int(usedStringConstants.GetLength() - 1); + stringIdToIndexMap.Insert(id, index); + return index; +} + +void asCWriter::WriteUsedStringConstants() +{ + TimeIt("asCWriter::WriteUsedStringConstants"); + + asUINT count = (asUINT)usedStringConstants.GetLength(); + WriteEncodedInt64(count); + for( asUINT i = 0; i < count; ++i ) + WriteString(engine->stringConstants[usedStringConstants[i]]); +} + +void asCWriter::WriteUsedFunctions() +{ + TimeIt("asCWriter::WriteUsedFunctions"); + + asUINT count = (asUINT)usedFunctions.GetLength(); + WriteEncodedInt64(count); + + for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) + { + char c; + + // Write enough data to be able to uniquely identify the function upon load + + if( usedFunctions[n] ) + { + // Is the function from the module or the application? + c = usedFunctions[n]->module ? 'm' : 'a'; + WriteData(&c, 1); + WriteFunctionSignature(usedFunctions[n]); + } + else + { + // null function pointer + c = 'n'; + WriteData(&c, 1); + } + } +} + +void asCWriter::WriteFunctionSignature(asCScriptFunction *func) +{ + asUINT i, count; + + WriteString(&func->name); + if( func->name == DELEGATE_FACTORY ) + { + // It's not necessary to write anything else + return; + } + + WriteDataType(&func->returnType); + + count = (asUINT)func->parameterTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + WriteDataType(&func->parameterTypes[i]); + + // Only write the inout flags if any of them are set + count = 0; + for( i = asUINT(func->inOutFlags.GetLength()); i > 0; i-- ) + if( func->inOutFlags[i-1] != asTM_NONE ) + { + count = i; + break; + } + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + WriteEncodedInt64(func->inOutFlags[i]); + + WriteEncodedInt64(func->funcType); + + // Write the default args, from last to first + count = 0; + for( i = (asUINT)func->defaultArgs.GetLength(); i-- > 0; ) + if( func->defaultArgs[i] ) + count++; + WriteEncodedInt64(count); + for( i = (asUINT)func->defaultArgs.GetLength(); i-- > 0; ) + if( func->defaultArgs[i] ) + WriteString(func->defaultArgs[i]); + + WriteTypeInfo(func->objectType); + + if( func->objectType ) + { + asBYTE b = 0; + b += func->isReadOnly ? 1 : 0; + b += func->isPrivate ? 2 : 0; + b += func->isProtected ? 4 : 0; + WriteData(&b, 1); + } + else + { + if (func->funcType == asFUNC_FUNCDEF) + { + if (func->nameSpace) + { + // This funcdef was declared as global entity + asBYTE b = 'n'; + WriteData(&b, 1); + WriteString(&func->nameSpace->name); + } + else + { + // This funcdef was declared as class member + asBYTE b = 'o'; + WriteData(&b, 1); + WriteTypeInfo(func->funcdefType->parentClass); + } + } + else + WriteString(&func->nameSpace->name); + } +} + +void asCWriter::WriteFunction(asCScriptFunction* func) +{ + char c; + + // If there is no function, then store a null char + if( func == 0 ) + { + c = '\0'; + WriteData(&c, 1); + return; + } + + // First check if the function has been saved already + for( asUINT f = 0; f < savedFunctions.GetLength(); f++ ) + { + if( savedFunctions[f] == func ) + { + c = 'r'; + WriteData(&c, 1); + WriteEncodedInt64(f); + return; + } + } + + // Keep a reference to the function in the list + savedFunctions.PushLast(func); + + c = 'f'; + WriteData(&c, 1); + + asUINT i, count; + + WriteFunctionSignature(func); + + if( func->funcType == asFUNC_SCRIPT ) + { + // Calculate the adjustment by position lookup table + CalculateAdjustmentByPos(func); + + WriteByteCode(func); + + asDWORD varSpace = AdjustStackPosition(func->scriptData->variableSpace); + WriteEncodedInt64(varSpace); + + count = (asUINT)func->scriptData->objVariablePos.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + { + WriteTypeInfo(func->scriptData->objVariableTypes[i]); + WriteEncodedInt64(AdjustStackPosition(func->scriptData->objVariablePos[i])); + } + if( count > 0 ) + WriteEncodedInt64(func->scriptData->objVariablesOnHeap); + + WriteEncodedInt64((asUINT)func->scriptData->objVariableInfo.GetLength()); + for( i = 0; i < func->scriptData->objVariableInfo.GetLength(); ++i ) + { + // The program position must be adjusted to be in number of instructions + WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->objVariableInfo[i].programPos]); + WriteEncodedInt64(AdjustStackPosition(func->scriptData->objVariableInfo[i].variableOffset)); + WriteEncodedInt64(func->scriptData->objVariableInfo[i].option); + } + + // The program position (every even number) needs to be adjusted + // to be in number of instructions instead of DWORD offset + if( !stripDebugInfo ) + { + asUINT length = (asUINT)func->scriptData->lineNumbers.GetLength(); + WriteEncodedInt64(length); + for( i = 0; i < length; ++i ) + { + if( (i & 1) == 0 ) + WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->lineNumbers[i]]); + else + WriteEncodedInt64(func->scriptData->lineNumbers[i]); + } + + // Write the array of script sections + length = (asUINT)func->scriptData->sectionIdxs.GetLength(); + WriteEncodedInt64(length); + for( i = 0; i < length; ++i ) + { + if( (i & 1) == 0 ) + WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->sectionIdxs[i]]); + else + { + if( func->scriptData->sectionIdxs[i] >= 0 ) + WriteString(engine->scriptSectionNames[func->scriptData->sectionIdxs[i]]); + else + { + c = 0; + WriteData(&c, 1); + } + } + } + } + + // Write the variable information + if( !stripDebugInfo ) + { + WriteEncodedInt64((asUINT)func->scriptData->variables.GetLength()); + for( i = 0; i < func->scriptData->variables.GetLength(); i++ ) + { + // The program position must be adjusted to be in number of instructions + WriteEncodedInt64(bytecodeNbrByPos[func->scriptData->variables[i]->declaredAtProgramPos]); + // The stack position must be adjusted according to the pointer sizes + WriteEncodedInt64(AdjustStackPosition(func->scriptData->variables[i]->stackOffset)); + WriteString(&func->scriptData->variables[i]->name); + WriteDataType(&func->scriptData->variables[i]->type); + } + } + + char bits = 0; + bits += func->isShared ? 1 : 0; + bits += func->dontCleanUpOnException ? 2 : 0; + WriteData(&bits,1); + + // Store script section name + if( !stripDebugInfo ) + { + if( func->scriptData->scriptSectionIdx >= 0 ) + WriteString(engine->scriptSectionNames[func->scriptData->scriptSectionIdx]); + else + { + c = 0; + WriteData(&c, 1); + } + WriteEncodedInt64(func->scriptData->declaredAt); + } + + // Store the parameter names + if( !stripDebugInfo ) + { + count = asUINT(func->parameterNames.GetLength()); + WriteEncodedInt64(count); + for( asUINT n = 0; n < count; n++ ) + WriteString(&func->parameterNames[n]); + } + } + else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE ) + { + // TODO: Do we really need to store this? It can probably be reconstructed by the reader + WriteEncodedInt64(func->vfTableIdx); + } + else if( func->funcType == asFUNC_FUNCDEF ) + { + char bits = 0; + bits += func->isShared ? 1 : 0; + WriteData(&bits,1); + } +} + +void asCWriter::WriteTypeDeclaration(asCTypeInfo *type, int phase) +{ + if( phase == 1 ) + { + // name + WriteString(&type->name); + // flags + WriteData(&type->flags, 4); + + // size + // TODO: Do we really need to store this? The reader should be able to + // determine the correct size from the object type's flags + if( (type->flags & asOBJ_SCRIPT_OBJECT) && type->size > 0 ) + { + // The size for script objects may vary from platform to platform so + // only store 1 to diferentiate from interfaces that have size 0. + WriteEncodedInt64(1); + } + else + { + // Enums, typedefs, and interfaces have fixed sizes independently + // of platform so it is safe to serialize the size directly. + WriteEncodedInt64(type->size); + } + + // namespace + WriteString(&type->nameSpace->name); + } + else if( phase == 2 ) + { + if(type->flags & asOBJ_ENUM ) + { + // enumValues[] + asCEnumType *t = CastToEnumType(type); + int size = (int)t->enumValues.GetLength(); + WriteEncodedInt64(size); + + for( int n = 0; n < size; n++ ) + { + WriteString(&t->enumValues[n]->name); + WriteData(&t->enumValues[n]->value, 4); + } + } + else if(type->flags & asOBJ_TYPEDEF ) + { + asCTypedefType *td = CastToTypedefType(type); + eTokenType t = td->aliasForType.GetTokenType(); + WriteEncodedInt64(t); + } + else + { + asCObjectType *t = CastToObjectType(type); + WriteTypeInfo(t->derivedFrom); + + // interfaces[] / interfaceVFTOffsets[] + // TOOD: Is it really necessary to store the VFTOffsets? Can't the reader calculate those? + int size = (asUINT)t->interfaces.GetLength(); + WriteEncodedInt64(size); + asUINT n; + asASSERT( t->interfaces.GetLength() == t->interfaceVFTOffsets.GetLength() ); + for( n = 0; n < t->interfaces.GetLength(); n++ ) + { + WriteTypeInfo(t->interfaces[n]); + WriteEncodedInt64(t->interfaceVFTOffsets[n]); + } + + // behaviours + // TODO: Default behaviours should just be stored as a indicator + // to avoid storing the actual function object + if( !t->IsInterface() && type->flags != asOBJ_TYPEDEF && type->flags != asOBJ_ENUM ) + { + WriteFunction(engine->scriptFunctions[t->beh.destruct]); + size = (int)t->beh.constructors.GetLength(); + WriteEncodedInt64(size); + for( n = 0; n < t->beh.constructors.GetLength(); n++ ) + { + WriteFunction(engine->scriptFunctions[t->beh.constructors[n]]); + WriteFunction(engine->scriptFunctions[t->beh.factories[n]]); + } + } + + // methods[] + // TODO: Avoid storing inherited methods in interfaces, as the reader + // can add those directly from the base interface + size = (int)t->methods.GetLength(); + WriteEncodedInt64(size); + for( n = 0; n < t->methods.GetLength(); n++ ) + { + WriteFunction(engine->scriptFunctions[t->methods[n]]); + } + + // virtualFunctionTable[] + // TODO: Is it really necessary to store this? Can't it be easily rebuilt by the reader + size = (int)t->virtualFunctionTable.GetLength(); + WriteEncodedInt64(size); + for( n = 0; n < (asUINT)size; n++ ) + { + WriteFunction(t->virtualFunctionTable[n]); + } + } + } + else if( phase == 3 ) + { + // properties[] + asCObjectType *t = CastToObjectType(type); + + // This is only done for object types + asASSERT(t); + + asUINT size = (asUINT)t->properties.GetLength(); + WriteEncodedInt64(size); + for (asUINT n = 0; n < t->properties.GetLength(); n++) + { + WriteObjectProperty(t->properties[n]); + } + } +} + +void asCWriter::WriteEncodedInt64(asINT64 i) +{ + asBYTE signBit = ( i & asINT64(1)<<63 ) ? 0x80 : 0; + if( signBit ) i = -i; + + asBYTE b; + if( i < (1<<6) ) + { + b = (asBYTE)(signBit + i); WriteData(&b, 1); + } + else if( i < (1<<13) ) + { + b = asBYTE(0x40 + signBit + (i >> 8)); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else if( i < (1<<20) ) + { + b = asBYTE(0x60 + signBit + (i >> 16)); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else if( i < (1<<27) ) + { + b = asBYTE(0x70 + signBit + (i >> 24)); WriteData(&b, 1); + b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else if( i < (asINT64(1)<<34) ) + { + b = asBYTE(0x78 + signBit + (i >> 32)); WriteData(&b, 1); + b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else if( i < (asINT64(1)<<41) ) + { + b = asBYTE(0x7C + signBit + (i >> 40)); WriteData(&b, 1); + b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else if( i < (asINT64(1)<<48) ) + { + b = asBYTE(0x7E + signBit + (i >> 48)); WriteData(&b, 1); + b = asBYTE((i >> 40) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } + else + { + b = asBYTE(0x7F + signBit); WriteData(&b, 1); + b = asBYTE((i >> 56) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 48) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 40) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 32) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 24) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 16) & 0xFF); WriteData(&b, 1); + b = asBYTE((i >> 8) & 0xFF); WriteData(&b, 1); + b = asBYTE(i & 0xFF); WriteData(&b, 1); + } +} + +void asCWriter::WriteString(asCString* str) +{ + // First check if the string hasn't been saved already + asSMapNode *cursor = 0; + if (stringToIdMap.MoveTo(&cursor, asCStringPointer(str))) + { + // Save a reference to the existing string + // The lowest bit is set to 1 to indicate a reference + WriteEncodedInt64(cursor->value*2+1); + return; + } + + // Save a new string + // The lowest bit is set to 0 to indicate a new string + asUINT len = (asUINT)str->GetLength(); + WriteEncodedInt64(len*2); + + if( len > 0 ) + { + stream->Write(str->AddressOf(), (asUINT)len); + + savedStrings.PushLast(*str); + stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1); + } +} + +void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop) +{ + // TODO: We might be able to avoid storing the name and type of the global + // properties twice if we merge this with the WriteUsedGlobalProperties. + WriteString(&prop->name); + WriteString(&prop->nameSpace->name); + WriteDataType(&prop->type); + + // Store the initialization function + WriteFunction(prop->GetInitFunc()); +} + +void asCWriter::WriteObjectProperty(asCObjectProperty* prop) +{ + WriteString(&prop->name); + WriteDataType(&prop->type); + int flags = 0; + if( prop->isPrivate ) flags |= 1; + if( prop->isProtected ) flags |= 2; + if( prop->isInherited ) flags |= 4; + WriteEncodedInt64(flags); +} + +void asCWriter::WriteDataType(const asCDataType *dt) +{ + // First check if the datatype has already been saved + for( asUINT n = 0; n < savedDataTypes.GetLength(); n++ ) + { + if( *dt == savedDataTypes[n] ) + { + WriteEncodedInt64(n+1); + return; + } + } + + // Indicate a new type with a null byte + asUINT c = 0; + WriteEncodedInt64(c); + + // Save the new datatype + savedDataTypes.PushLast(*dt); + + int t = dt->GetTokenType(); + WriteEncodedInt64(t); + if( t == ttIdentifier ) + WriteTypeInfo(dt->GetTypeInfo()); + + // Endianess safe bitmask + char bits = 0; + SAVE_TO_BIT(bits, dt->IsObjectHandle(), 0); + SAVE_TO_BIT(bits, dt->IsHandleToConst(), 1); + SAVE_TO_BIT(bits, dt->IsReference(), 2); + SAVE_TO_BIT(bits, dt->IsReadOnly(), 3); + WriteData(&bits, 1); +} + +void asCWriter::WriteTypeInfo(asCTypeInfo* ti) +{ + char ch; + + if( ti ) + { + // Check for template instances/specializations + asCObjectType *ot = CastToObjectType(ti); + if( ot && ot->templateSubTypes.GetLength() ) + { + // Check for list pattern type or template type + if( ot->flags & asOBJ_LIST_PATTERN ) + { + ch = 'l'; // list + WriteData(&ch, 1); + WriteTypeInfo(ot->templateSubTypes[0].GetTypeInfo()); + } + else + { + ch = 'a'; // array + WriteData(&ch, 1); + WriteString(&ot->name); + WriteString(&ot->nameSpace->name); + + WriteEncodedInt64(ot->templateSubTypes.GetLength()); + for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) + { + if( !ot->templateSubTypes[n].IsPrimitive() || ot->templateSubTypes[n].IsEnumType() ) + { + ch = 's'; // sub type + WriteData(&ch, 1); + WriteDataType(&ot->templateSubTypes[n]); + } + else + { + ch = 't'; // token + WriteData(&ch, 1); + eTokenType t = ot->templateSubTypes[n].GetTokenType(); + WriteEncodedInt64(t); + } + } + } + } + else if( ti->flags & asOBJ_TEMPLATE_SUBTYPE ) + { + ch = 's'; // sub type + WriteData(&ch, 1); + WriteString(&ti->name); + } + else if( ti->nameSpace ) + { + ch = 'o'; // object + WriteData(&ch, 1); + WriteString(&ti->name); + WriteString(&ti->nameSpace->name); + } + else + { + asASSERT(ti->flags & asOBJ_FUNCDEF); + + ch = 'c'; // child type + WriteData(&ch, 1); + WriteString(&ti->name); + WriteTypeInfo(CastToFuncdefType(ti)->parentClass); + } + } + else + { + ch = '\0'; + WriteData(&ch, 1); + } +} + +void asCWriter::CalculateAdjustmentByPos(asCScriptFunction *func) +{ + // Adjust the offset of all negative variables (parameters) so all pointers will have a size of 1 dword + asUINT n; + asCArray adjustments; + asUINT offset = 0; + if( func->objectType ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += AS_PTR_SIZE; + } + if( func->DoesReturnOnStack() ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += AS_PTR_SIZE; + } + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( !func->parameterTypes[n].IsPrimitive() || + func->parameterTypes[n].IsReference() ) + { + adjustments.PushLast(offset); + adjustments.PushLast(1-AS_PTR_SIZE); + offset += AS_PTR_SIZE; + } + else + { + asASSERT( func->parameterTypes[n].IsPrimitive() ); + offset += func->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + // Build look-up table with the adjustments for each stack position + adjustNegativeStackByPos.SetLength(offset); + memset(adjustNegativeStackByPos.AddressOf(), 0, adjustNegativeStackByPos.GetLength()*sizeof(int)); + for( n = 0; n < adjustments.GetLength(); n+=2 ) + { + int pos = adjustments[n]; + int adjust = adjustments[n+1]; + + for( asUINT i = pos+1; i < adjustNegativeStackByPos.GetLength(); i++ ) + adjustNegativeStackByPos[i] += adjust; + } + + // Adjust the offset of all positive variables so that all object types and handles have a size of 1 dword + // This is similar to how the adjustment is done in the asCReader::TranslateFunction, only the reverse + adjustments.SetLength(0); + for( n = 0; n < func->scriptData->objVariableTypes.GetLength(); n++ ) + { + // Determine the size the variable currently occupies on the stack + int size = AS_PTR_SIZE; + + // objVariableTypes is null if the variable type is a null pointer + if( func->scriptData->objVariableTypes[n] && + (func->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) && + n >= func->scriptData->objVariablesOnHeap ) + { + size = func->scriptData->objVariableTypes[n]->GetSize(); + if( size < 4 ) + size = 1; + else + size /= 4; + } + + // If larger than 1 dword, adjust the offsets accordingly + if (size > 1) + { + // How much needs to be adjusted? + adjustments.PushLast(func->scriptData->objVariablePos[n]); + adjustments.PushLast(-(size - 1)); + } + } + + // Build look-up table with the adjustments for each stack position + adjustStackByPos.SetLength(func->scriptData->stackNeeded); + memset(adjustStackByPos.AddressOf(), 0, adjustStackByPos.GetLength()*sizeof(int)); + for( n = 0; n < adjustments.GetLength(); n+=2 ) + { + int pos = adjustments[n]; + int adjust = adjustments[n+1]; + + for( asUINT i = pos; i < adjustStackByPos.GetLength(); i++ ) + adjustStackByPos[i] += adjust; + } + + // Compute the sequence number of each bytecode instruction in order to update the jump offsets + asUINT length = func->scriptData->byteCode.GetLength(); + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + bytecodeNbrByPos.SetLength(length); + asUINT num; + for( offset = 0, num = 0; offset < length; ) + { + bytecodeNbrByPos[offset] = num; + offset += asBCTypeSize[asBCInfo[*(asBYTE*)(bc+offset)].type]; + num++; + } + // The last instruction is always a BC_RET. This make it possible to query + // the number of instructions by checking the last entry in bytecodeNbrByPos + asASSERT(*(asBYTE*)(bc+length-1) == asBC_RET); +} + +int asCWriter::AdjustStackPosition(int pos) +{ + if( pos >= (int)adjustStackByPos.GetLength() ) + { + // This happens for example if the function only have temporary variables + // The adjustByPos can also be empty if the function doesn't have any variables at all, but receive a handle by parameter + if( adjustStackByPos.GetLength() > 0 ) + pos += adjustStackByPos[adjustStackByPos.GetLength()-1]; + } + else if( pos >= 0 ) + pos += adjustStackByPos[pos]; + else + { + asASSERT( -pos < (int)adjustNegativeStackByPos.GetLength() ); + pos -= (short)adjustNegativeStackByPos[-pos]; + } + + return pos; +} + +int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD programPos) +{ + // TODO: optimize: multiple instructions for the same function doesn't need to look for the function everytime + // the function can remember where it found the function and check if the programPos is still valid + + // Get offset 0 doesn't need adjustment + if( offset == 0 ) return 0; + + bool bcAlloc = false; + + // Find out which function that will be called + asCScriptFunction *calledFunc = 0; + int stackDelta = 0; + for( asUINT n = programPos; n < func->scriptData->byteCode.GetLength(); ) + { + asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; + if( bc == asBC_CALL || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || + bc == asBC_CALLINTF ) + { + // Find the function from the function id in bytecode + int funcId = asBC_INTARG(&func->scriptData->byteCode[n]); + calledFunc = engine->scriptFunctions[funcId]; + break; + } + else if( bc == asBC_ALLOC ) + { + // The alloc instruction doesn't take the object pointer on the stack, + // as the memory will be allocated by the instruction itself + bcAlloc = true; + + // Find the function from the function id in the bytecode + int funcId = asBC_INTARG(&func->scriptData->byteCode[n+AS_PTR_SIZE]); + calledFunc = engine->scriptFunctions[funcId]; + break; + } + else if( bc == asBC_CALLBND ) + { + // Find the function from the engine's bind array + int funcId = asBC_INTARG(&func->scriptData->byteCode[n]); + calledFunc = engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature; + break; + } + else if( bc == asBC_CallPtr ) + { + int var = asBC_SWORDARG0(&func->scriptData->byteCode[n]); + asUINT v; + // Find the funcdef from the local variable + for( v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ ) + { + if( func->scriptData->objVariablePos[v] == var ) + { + calledFunc = CastToFuncdefType(func->scriptData->objVariableTypes[v])->funcdef; + break; + } + } + if( !calledFunc ) + { + // Look in parameters + int paramPos = 0; + if( func->objectType ) + paramPos -= AS_PTR_SIZE; + if( func->DoesReturnOnStack() ) + paramPos -= AS_PTR_SIZE; + for( v = 0; v < func->parameterTypes.GetLength(); v++ ) + { + if( var == paramPos ) + { + calledFunc = CastToFuncdefType(func->parameterTypes[v].GetTypeInfo())->funcdef; + break; + } + paramPos -= func->parameterTypes[v].GetSizeOnStackDWords(); + } + } + break; + } + else if( bc == asBC_REFCPY || + bc == asBC_COPY ) + { + // In this case we know there is only 1 pointer on the stack above + asASSERT( offset == AS_PTR_SIZE ); + return offset + (1 - AS_PTR_SIZE); + } + + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + + n += asBCTypeSize[asBCInfo[bc].type]; + } + + asASSERT( calledFunc ); + + // Count the number of pointers pushed on the stack above the + // current offset, and then adjust the offset accordingly + asUINT numPtrs = 0; + int currOffset = -stackDelta; + if( offset > currOffset && calledFunc->GetObjectType() && !bcAlloc ) + { + currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; + } + if( offset > currOffset && calledFunc->DoesReturnOnStack() ) + { + currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; + } + for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) + { + if( offset <= currOffset ) break; + + if( !calledFunc->parameterTypes[p].IsPrimitive() || + calledFunc->parameterTypes[p].IsReference() ) + { + // objects and references are passed by pointer + currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; + + // The variable arg ? has an additional 32bit int with the typeid + if( calledFunc->parameterTypes[p].IsAnyType() ) + currOffset += 1; + } + else + { + // built-in primitives or enums are passed by value + asASSERT( calledFunc->parameterTypes[p].IsPrimitive() ); + currOffset += calledFunc->parameterTypes[p].GetSizeOnStackDWords(); + } + } + + // The get offset must match one of the parameter offsets + asASSERT( offset == currOffset ); + + return offset + numPtrs * (1 - AS_PTR_SIZE); +} + +void asCWriter::WriteByteCode(asCScriptFunction *func) +{ + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + size_t length = func->scriptData->byteCode.GetLength(); + + // The length cannot be stored, because it is platform dependent, + // instead we store the number of instructions + asUINT count = bytecodeNbrByPos[bytecodeNbrByPos.GetLength()-1] + 1; + WriteEncodedInt64(count); + + asDWORD *startBC = bc; + while( length ) + { + asDWORD tmpBC[4]; // The biggest instructions take up 4 DWORDs + asDWORD c = *(asBYTE*)bc; + + // Copy the instruction to a temp buffer so we can work on it before saving + memcpy(tmpBC, bc, asBCTypeSize[asBCInfo[c].type]*sizeof(asDWORD)); + + if( c == asBC_ALLOC ) // PTR_DW_ARG + { + // Translate the object type + asCObjectType *ot = *(asCObjectType**)(tmpBC+1); + *(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(ot); + + // Translate the constructor func id, unless it is 0 + if( *(int*)&tmpBC[1+AS_PTR_SIZE] != 0 ) + { + // Increment 1 to the translated function id, as 0 will be reserved for no function + *(int*)&tmpBC[1+AS_PTR_SIZE] = 1+FindFunctionIndex(engine->scriptFunctions[*(int*)&tmpBC[1+AS_PTR_SIZE]]); + } + } + else if( c == asBC_REFCPY || // PTR_ARG + c == asBC_RefCpyV || // wW_PTR_ARG + c == asBC_OBJTYPE ) // PTR_ARG + { + // Translate object type pointers into indices + *(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(*(asCObjectType**)(tmpBC+1)); + } + else if( c == asBC_JitEntry ) // PTR_ARG + { + // We don't store the JIT argument + *(asPWORD*)(tmpBC+1) = 0; + } + else if( c == asBC_TYPEID || // DW_ARG + c == asBC_Cast ) // DW_ARG + { + // Translate type ids into indices + *(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1)); + } + else if( c == asBC_ADDSi || // W_DW_ARG + c == asBC_LoadThisR ) // W_DW_ARG + { + // Translate property offsets into indices + *(((short*)tmpBC)+1) = (short)FindObjectPropIndex(*(((short*)tmpBC)+1), *(int*)(tmpBC+1)); + + // Translate type ids into indices + *(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1)); + } + else if( c == asBC_LoadRObjR || // rW_W_DW_ARG + c == asBC_LoadVObjR ) // rW_W_DW_ARG + { + asCObjectType *ot = engine->GetObjectTypeFromTypeId(*(int*)(tmpBC+2)); + if( ot->flags & asOBJ_LIST_PATTERN ) + { + // List patterns have a different way of translating the offsets + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + *(((short*)tmpBC)+2) = (short)listAdj->AdjustOffset(*(((short*)tmpBC)+2), ot); + } + else + { + // Translate property offsets into indices + // TODO: optimize: Pass the object type directly to the method instead of the type id + *(((short*)tmpBC)+2) = (short)FindObjectPropIndex(*(((short*)tmpBC)+2), *(int*)(tmpBC+2)); + } + + // Translate type ids into indices + *(int*)(tmpBC+2) = FindTypeIdIdx(*(int*)(tmpBC+2)); + } + else if( c == asBC_COPY ) // W_DW_ARG + { + // Translate type ids into indices + *(int*)(tmpBC+1) = FindTypeIdIdx(*(int*)(tmpBC+1)); + + // Update the WORDARG0 to 0, as this will be recalculated on the target platform + asBC_WORDARG0(tmpBC) = 0; + } + else if( c == asBC_RET ) // W_ARG + { + // Save with arg 0, as this will be recalculated on the target platform + asBC_WORDARG0(tmpBC) = 0; + } + else if( c == asBC_CALL || // DW_ARG + c == asBC_CALLINTF || // DW_ARG + c == asBC_CALLSYS || // DW_ARG + c == asBC_Thiscall1 ) // DW_ARG + { + // Translate the function id + *(int*)(tmpBC+1) = FindFunctionIndex(engine->scriptFunctions[*(int*)(tmpBC+1)]); + } + else if( c == asBC_FuncPtr ) // PTR_ARG + { + // Translate the function pointer + *(asPWORD*)(tmpBC+1) = FindFunctionIndex(*(asCScriptFunction**)(tmpBC+1)); + } + else if( c == asBC_STR ) // W_ARG + { + // Translate the string constant id + asWORD *arg = ((asWORD*)tmpBC)+1; + *arg = (asWORD)FindStringConstantIndex(*arg); + } + else if( c == asBC_CALLBND ) // DW_ARG + { + // Translate the function id + int funcId = tmpBC[1]; + for( asUINT n = 0; n < module->bindInformations.GetLength(); n++ ) + if( module->bindInformations[n]->importedFunctionSignature->id == funcId ) + { + funcId = n; + break; + } + + tmpBC[1] = funcId; + } + else if( c == asBC_PGA || // PTR_ARG + c == asBC_PshGPtr || // PTR_ARG + c == asBC_LDG || // PTR_ARG + c == asBC_PshG4 || // PTR_ARG + c == asBC_LdGRdR4 || // wW_PTR_ARG + c == asBC_CpyGtoV4 || // wW_PTR_ARG + c == asBC_CpyVtoG4 || // rW_PTR_ARG + c == asBC_SetG4 ) // PTR_DW_ARG + { + // Translate global variable pointers into indices + *(asPWORD*)(tmpBC+1) = FindGlobalPropPtrIndex(*(void**)(tmpBC+1)); + } + else if( c == asBC_JMP || // DW_ARG + c == asBC_JZ || + c == asBC_JNZ || + c == asBC_JLowZ || + c == asBC_JLowNZ || + c == asBC_JS || + c == asBC_JNS || + c == asBC_JP || + c == asBC_JNP ) // The JMPP instruction doesn't need modification + { + // Get the DWORD offset from arg + int offset = *(int*)(tmpBC+1); + + // Determine instruction number for next instruction and destination + int bcSeqNum = bytecodeNbrByPos[asUINT(bc - startBC)] + 1; + asDWORD *targetBC = bc + 2 + offset; + int targetBcSeqNum = bytecodeNbrByPos[asUINT(targetBC - startBC)]; + + // Set the offset in number of instructions + *(int*)(tmpBC+1) = targetBcSeqNum - bcSeqNum; + } + else if( c == asBC_GETOBJ || // W_ARG + c == asBC_GETOBJREF || + c == asBC_GETREF || + c == asBC_ChkNullS ) + { + // Adjust the offset according to the function call that comes after + asBC_WORDARG0(tmpBC) = (asWORD)AdjustGetOffset(asBC_WORDARG0(tmpBC), func, asDWORD(bc - startBC)); + } + else if( c == asBC_AllocMem ) + { + // It's not necessary to store the size of the list buffer, as it will be recalculated in the reader + asBC_DWORDARG(tmpBC) = 0; + + // Determine the type of the list pattern from the variable + short var = asBC_WORDARG0(tmpBC); + asCObjectType *ot = CastToObjectType(func->GetTypeInfoOfLocalVar(var)); + + // Create this helper object to adjust the offset of the elements accessed in the buffer + listAdjusters.PushLast(asNEW(SListAdjuster)(ot)); + } + else if( c == asBC_FREE ) // wW_PTR_ARG + { + // Translate object type pointers into indices + asCObjectType *ot = *(asCObjectType**)(tmpBC+1); + *(asPWORD*)(tmpBC+1) = FindTypeInfoIdx(ot); + + // Pop and destroy the list adjuster helper that was created with asBC_AllocMem + if( ot && (ot->flags & asOBJ_LIST_PATTERN) ) + { + SListAdjuster *list = listAdjusters.PopLast(); + asDELETE(list, SListAdjuster); + } + } + else if( c == asBC_SetListSize ) + { + // Adjust the offset in the initialization list + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType); + + // Tell the adjuster how many repeated values there are + listAdj->SetRepeatCount(tmpBC[2]); + } + else if( c == asBC_PshListElmnt ) // W_DW_ARG + { + // Adjust the offset in the initialization list + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType); + } + else if( c == asBC_SetListType ) + { + // Adjust the offset in the initialization list + SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; + tmpBC[1] = listAdj->AdjustOffset(tmpBC[1], listAdj->patternType); + + // Inform the adjuster of the type id of the next element + listAdj->SetNextType(tmpBC[2]); + + // Translate the type id + tmpBC[2] = FindTypeIdIdx(tmpBC[2]); + } + // Adjust the variable offsets + switch( asBCInfo[c].type ) + { + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_ARG: + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_wW_W_ARG: + case asBCTYPE_rW_QW_ARG: + case asBCTYPE_rW_W_DW_ARG: + case asBCTYPE_rW_DW_DW_ARG: + { + asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC)); + } + break; + + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_rW_ARG: + { + asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC)); + asBC_SWORDARG1(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG1(tmpBC)); + } + break; + + case asBCTYPE_wW_rW_rW_ARG: + { + asBC_SWORDARG0(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG0(tmpBC)); + asBC_SWORDARG1(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG1(tmpBC)); + asBC_SWORDARG2(tmpBC) = (short)AdjustStackPosition(asBC_SWORDARG2(tmpBC)); + } + break; + + default: + // The other types don't treat variables so won't be modified + break; + } + + // TODO: bytecode: Must make sure that floats and doubles are always stored the same way regardless of platform. + // Some platforms may not use the IEEE 754 standard, in which case it is necessary to encode the values + + // Now store the instruction in the smallest possible way + switch( asBCInfo[c].type ) + { + case asBCTYPE_NO_ARG: + { + // Just write 1 byte + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + } + break; + case asBCTYPE_W_ARG: + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + } + break; + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_W_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the word argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the dword argument + WriteEncodedInt64((int)tmpBC[1]); + } + break; + case asBCTYPE_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the argument + WriteEncodedInt64((int)tmpBC[1]); + } + break; + case asBCTYPE_DW_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the dword argument + WriteEncodedInt64((int)tmpBC[1]); + + // Write the dword argument + WriteEncodedInt64((int)tmpBC[2]); + } + break; + case asBCTYPE_wW_rW_rW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the first argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the second argument + w = *(((short*)tmpBC)+2); + WriteEncodedInt64(w); + + // Write the third argument + w = *(((short*)tmpBC)+3); + WriteEncodedInt64(w); + } + break; + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + case asBCTYPE_wW_W_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the first argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the second argument + w = *(((short*)tmpBC)+2); + WriteEncodedInt64(w); + } + break; + case asBCTYPE_wW_rW_DW_ARG: + case asBCTYPE_rW_W_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the first argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the second argument + w = *(((short*)tmpBC)+2); + WriteEncodedInt64(w); + + // Write the third argument + int dw = tmpBC[2]; + WriteEncodedInt64(dw); + } + break; + case asBCTYPE_QW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the argument + asQWORD qw = *(asQWORD*)&tmpBC[1]; + WriteEncodedInt64(qw); + } + break; + case asBCTYPE_QW_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the argument + asQWORD qw = *(asQWORD*)&tmpBC[1]; + WriteEncodedInt64(qw); + + // Write the second argument + int dw = tmpBC[3]; + WriteEncodedInt64(dw); + } + break; + case asBCTYPE_rW_QW_ARG: + case asBCTYPE_wW_QW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the first argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the argument + asQWORD qw = *(asQWORD*)&tmpBC[1]; + WriteEncodedInt64(qw); + } + break; + case asBCTYPE_rW_DW_DW_ARG: + { + // Write the instruction code + asBYTE b = (asBYTE)c; + WriteData(&b, 1); + + // Write the short argument + short w = *(((short*)tmpBC)+1); + WriteEncodedInt64(w); + + // Write the dword argument + WriteEncodedInt64((int)tmpBC[1]); + + // Write the dword argument + WriteEncodedInt64((int)tmpBC[2]); + } + break; + default: + { + // This should never happen + asASSERT(false); + + // Store the bc as is + for( int n = 0; n < asBCTypeSize[asBCInfo[c].type]; n++ ) + WriteData(&tmpBC[n], 4); + } + } + + // Move to the next instruction + bc += asBCTypeSize[asBCInfo[c].type]; + length -= asBCTypeSize[asBCInfo[c].type]; + } +} + +asCWriter::SListAdjuster::SListAdjuster(asCObjectType *ot) : patternType(ot), repeatCount(0), entries(0), lastOffset(-1), nextOffset(0), nextTypeId(-1) +{ + asASSERT( ot && (ot->flags & asOBJ_LIST_PATTERN) ); + + // Find the first expected value in the list + asSListPatternNode *node = ot->engine->scriptFunctions[patternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern; + asASSERT( node && node->type == asLPT_START ); + patternNode = node->next; +} + +int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatternType) +{ + // TODO: cleanup: The listPatternType parameter is not needed + asASSERT( patternType == listPatternType ); + UNUSED_VAR(listPatternType); + + asASSERT( offset >= lastOffset ); + + // If it is the same offset being accessed again, just return the same adjusted value + if( offset == lastOffset ) + return entries-1; + + asASSERT( offset >= nextOffset ); + + // Update last offset for next call + lastOffset = offset; + + // What is being expected at this position? + if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) + { + // Don't move the patternNode yet because the caller must make a call to SetRepeatCount too + nextOffset = offset + 4; + return entries++; + } + else if( patternNode->type == asLPT_TYPE ) + { + const asCDataType &dt = reinterpret_cast(patternNode)->dataType; + if( dt.GetTokenType() == ttQuestion ) + { + // The bytecode need to inform the type that will + // come next and then adjust that position too before + // we can move to the next node + if( nextTypeId != -1 ) + { + nextOffset = offset + 4; + + if( repeatCount > 0 ) + repeatCount--; + + // Only move the patternNode if we're not expecting any more repeated entries + if( repeatCount == 0 ) + patternNode = patternNode->next; + + nextTypeId = -1; + } + } + else + { + if( repeatCount > 0 ) + { + // Was any value skipped? + asUINT size; + if( dt.IsObjectHandle() || (dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_REF)) ) + size = AS_PTR_SIZE*4; + else + size = dt.GetSizeInMemoryBytes(); + + int count = 0; + while( nextOffset <= offset ) + { + count++; + nextOffset += size; + + // Align the offset on 4 byte boundaries + if( size >= 4 && (nextOffset & 0x3) ) + nextOffset += 4 - (nextOffset & 0x3); + } + + if( --count > 0 ) + { + // Skip these values + repeatCount -= count; + entries += count; + } + + nextOffset = offset + size; + repeatCount--; + } + + // Only move the patternNode if we're not expecting any more repeated entries + if( repeatCount == 0 ) + patternNode = patternNode->next; + } + + return entries++; + } + else if( patternNode->type == asLPT_START ) + { + if( repeatCount > 0 ) + repeatCount--; + SInfo info = {repeatCount, patternNode}; + stack.PushLast(info); + + repeatCount = 0; + patternNode = patternNode->next; + + lastOffset--; + return AdjustOffset(offset, listPatternType); + } + else if( patternNode->type == asLPT_END ) + { + SInfo info = stack.PopLast(); + repeatCount = info.repeatCount; + if( repeatCount ) + patternNode = info.startNode; + else + patternNode = patternNode->next; + + lastOffset--; + return AdjustOffset(offset, listPatternType); + } + else + { + // Something is wrong with the pattern list declaration + asASSERT( false ); + } + + return 0; +} + +void asCWriter::SListAdjuster::SetRepeatCount(asUINT rc) +{ + // Make sure the list is expecting a repeat at this location + asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ); + + // Now move to the next patternNode + patternNode = patternNode->next; + + repeatCount = rc; +} + +void asCWriter::SListAdjuster::SetNextType(int typeId) +{ + // Make sure the list is expecting a type at this location + asASSERT( patternNode->type == asLPT_TYPE && + reinterpret_cast(patternNode)->dataType.GetTokenType() == ttQuestion ); + + // Inform the type id for the next adjustment + nextTypeId = typeId; +} + +void asCWriter::WriteUsedTypeIds() +{ + TimeIt("asCWriter::WriteUsedTypeIds"); + + asUINT count = (asUINT)usedTypeIds.GetLength(); + WriteEncodedInt64(count); + for( asUINT n = 0; n < count; n++ ) + { + asCDataType dt = engine->GetDataTypeFromTypeId(usedTypeIds[n]); + WriteDataType(&dt); + } +} + +int asCWriter::FindGlobalPropPtrIndex(void *ptr) +{ + int i = usedGlobalProperties.IndexOf(ptr); + if( i >= 0 ) return i; + + usedGlobalProperties.PushLast(ptr); + return (int)usedGlobalProperties.GetLength()-1; +} + +void asCWriter::WriteUsedGlobalProps() +{ + TimeIt("asCWriter::WriteUsedGlobalProps"); + + int c = (int)usedGlobalProperties.GetLength(); + WriteEncodedInt64(c); + + for( int n = 0; n < c; n++ ) + { + asPWORD *p = (asPWORD*)usedGlobalProperties[n]; + + // Find the property descriptor from the address + asCGlobalProperty *prop = 0; + asSMapNode *cursor; + if( engine->varAddressMap.MoveTo(&cursor, p) ) + { + prop = engine->varAddressMap.GetValue(cursor); + } + + asASSERT(prop); + + // Store the name and type of the property so we can find it again on loading + WriteString(&prop->name); + WriteString(&prop->nameSpace->name); + WriteDataType(&prop->type); + + // Also store whether the property is a module property or a registered property + char moduleProp = 0; + if( prop->realAddress == 0 ) + moduleProp = 1; + WriteData(&moduleProp, 1); + } +} + +void asCWriter::WriteUsedObjectProps() +{ + TimeIt("asCWriter::WriteUsedObjectProps"); + + int c = (int)usedObjectProperties.GetLength(); + WriteEncodedInt64(c); + + for( asUINT n = 0; n < usedObjectProperties.GetLength(); n++ ) + { + asCObjectType *objType = usedObjectProperties[n].objType; + WriteTypeInfo(objType); + + // Find the property name + for( asUINT p = 0; p < objType->properties.GetLength(); p++ ) + { + if( objType->properties[p]->byteOffset == usedObjectProperties[n].offset ) + { + WriteString(&objType->properties[p]->name); + break; + } + } + } +} + +int asCWriter::FindObjectPropIndex(short offset, int typeId) +{ + asCObjectType *objType = engine->GetObjectTypeFromTypeId(typeId); + for( asUINT n = 0; n < usedObjectProperties.GetLength(); n++ ) + { + if( usedObjectProperties[n].objType == objType && + usedObjectProperties[n].offset == offset ) + return n; + } + + SObjProp prop = {objType, offset}; + usedObjectProperties.PushLast(prop); + return (int)usedObjectProperties.GetLength() - 1; +} + +int asCWriter::FindFunctionIndex(asCScriptFunction *func) +{ + for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) + { + if( usedFunctions[n] == func ) + return n; + } + + usedFunctions.PushLast(func); + return (int)usedFunctions.GetLength() - 1; +} + +int asCWriter::FindTypeIdIdx(int typeId) +{ + asUINT n; + for( n = 0; n < usedTypeIds.GetLength(); n++ ) + { + if( usedTypeIds[n] == typeId ) + return n; + } + + usedTypeIds.PushLast(typeId); + return (int)usedTypeIds.GetLength() - 1; +} + +int asCWriter::FindTypeInfoIdx(asCTypeInfo *obj) +{ + asUINT n; + for( n = 0; n < usedTypes.GetLength(); n++ ) + { + if( usedTypes[n] == obj ) + return n; + } + + usedTypes.PushLast(obj); + return (int)usedTypes.GetLength() - 1; +} + +#endif // AS_NO_COMPILER + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_scriptcode.cpp b/3rdparty/angelscript/src/as_scriptcode.cpp new file mode 100644 index 0000000..833b401 --- /dev/null +++ b/3rdparty/angelscript/src/as_scriptcode.cpp @@ -0,0 +1,151 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptcode.cpp +// +// A container class for the script code to be compiled +// + + + +#include "as_config.h" +#include "as_scriptcode.h" + +BEGIN_AS_NAMESPACE + +asCScriptCode::asCScriptCode() +{ + lineOffset = 0; + code = 0; + codeLength = 0; + sharedCode = false; +} + +asCScriptCode::~asCScriptCode() +{ + if( !sharedCode && code ) + { + asDELETEARRAY(code); + } +} + +int asCScriptCode::SetCode(const char *in_name, const char *in_code, bool in_makeCopy) +{ + return SetCode(in_name, in_code, 0, in_makeCopy); +} + +int asCScriptCode::SetCode(const char *in_name, const char *in_code, size_t in_length, bool in_makeCopy) +{ + if( !in_code) return asINVALID_ARG; + this->name = in_name ? in_name : ""; + if( !sharedCode && code ) + asDELETEARRAY(code); + + if( in_length == 0 ) + in_length = strlen(in_code); + if( in_makeCopy ) + { + codeLength = in_length; + sharedCode = false; + code = asNEWARRAY(char, in_length); + if( code == 0 ) + return asOUT_OF_MEMORY; + memcpy(code, in_code, in_length); + } + else + { + codeLength = in_length; + code = const_cast(in_code); + sharedCode = true; + } + + // Find the positions of each line + linePositions.PushLast(0); + for( size_t n = 0; n < in_length; n++ ) + if( in_code[n] == '\n' ) linePositions.PushLast(n+1); + linePositions.PushLast(in_length); + + return asSUCCESS; +} + +void asCScriptCode::ConvertPosToRowCol(size_t pos, int *row, int *col) +{ + if( linePositions.GetLength() == 0 ) + { + if( row ) *row = lineOffset; + if( col ) *col = 1; + return; + } + + // Do a binary search in the buffer + int max = (int)linePositions.GetLength() - 1; + int min = 0; + int i = max/2; + + for(;;) + { + if( linePositions[i] < pos ) + { + // Have we found the largest number < programPosition? + if( min == i ) break; + + min = i; + i = (max + min)/2; + } + else if( linePositions[i] > pos ) + { + // Have we found the smallest number > programPoisition? + if( max == i ) break; + + max = i; + i = (max + min)/2; + } + else + { + // We found the exact position + break; + } + } + + if( row ) *row = i + 1 + lineOffset; + if( col ) *col = (int)(pos - linePositions[i]) + 1; +} + +bool asCScriptCode::TokenEquals(size_t pos, size_t len, const char *str) +{ + if( pos + len > codeLength ) return false; + if( strncmp(code + pos, str, len) == 0 && strlen(str) == len ) + return true; + return false; +} + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_scriptengine.cpp b/3rdparty/angelscript/src/as_scriptengine.cpp new file mode 100644 index 0000000..f1d6949 --- /dev/null +++ b/3rdparty/angelscript/src/as_scriptengine.cpp @@ -0,0 +1,6497 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptengine.cpp +// +// The implementation of the script engine interface +// + + +#include + +#include "as_config.h" +#include "as_scriptengine.h" +#include "as_builder.h" +#include "as_context.h" +#include "as_string_util.h" +#include "as_tokenizer.h" +#include "as_texts.h" +#include "as_module.h" +#include "as_callfunc.h" +#include "as_generic.h" +#include "as_scriptobject.h" +#include "as_compiler.h" +#include "as_bytecode.h" +#include "as_debug.h" + +BEGIN_AS_NAMESPACE + + + +#ifdef AS_PROFILE +// Instantiate the profiler once +CProfiler g_profiler; +#endif + + + + +extern "C" +{ + +AS_API const char * asGetLibraryVersion() +{ +#ifdef _DEBUG + return ANGELSCRIPT_VERSION_STRING " DEBUG"; +#else + return ANGELSCRIPT_VERSION_STRING; +#endif +} + +AS_API const char * asGetLibraryOptions() +{ + const char *string = " " + + // Options +#ifdef AS_MAX_PORTABILITY + "AS_MAX_PORTABILITY " +#endif +#ifdef AS_DEBUG + "AS_DEBUG " +#endif +#ifdef AS_NO_CLASS_METHODS + "AS_NO_CLASS_METHODS " +#endif +#ifdef AS_USE_DOUBLE_AS_FLOAT + "AS_USE_DOUBLE_AS_FLOAT " +#endif +#ifdef AS_64BIT_PTR + "AS_64BIT_PTR " +#endif +#ifdef AS_NO_THREADS + "AS_NO_THREADS " +#endif +#ifdef AS_NO_ATOMIC + "AS_NO_ATOMIC " +#endif +#ifdef AS_NO_COMPILER + "AS_NO_COMPILER " +#endif +#ifdef AS_NO_MEMBER_INIT + "AS_NO_MEMBER_INIT " +#endif +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + "AS_NO_THISCALL_FUNCTOR_METHOD " +#endif +#ifdef AS_NO_EXCEPTIONS + "AS_NO_EXCEPTIONS " +#endif +#ifdef WIP_16BYTE_ALIGN + "WIP_16BYTE_ALIGN " +#endif +#ifdef AS_BIG_ENDIAN + "AS_BIG_ENDIAN " +#endif + + // Target system +#ifdef AS_WIN + "AS_WIN " +#endif +#ifdef AS_LINUX + "AS_LINUX " +#endif +#ifdef AS_MAC + "AS_MAC " +#endif +#ifdef AS_SUN + "AS_SUN " +#endif +#ifdef AS_BSD + "AS_BSD " +#endif +#ifdef AS_XBOX + "AS_XBOX " +#endif +#ifdef AS_XBOX360 + "AS_XBOX360 " +#endif +#ifdef AS_PSP + "AS_PSP " +#endif +#ifdef AS_PS2 + "AS_PS2 " +#endif +#ifdef AS_PS3 + "AS_PS3 " +#endif +#ifdef AS_PSVITA + "AS_PSVITA " +#endif +#ifdef AS_DC + "AS_DC " +#endif +#ifdef AS_GC + "AS_GC " +#endif +#ifdef AS_WII + "AS_WII " +#endif +#ifdef AS_WIIU + "AS_WIIU " +#endif +#ifdef AS_IPHONE + "AS_IPHONE " +#endif +#ifdef AS_ANDROID + "AS_ANDROID " +#endif +#ifdef AS_HAIKU + "AS_HAIKU " +#endif +#ifdef AS_ILLUMOS + "AS_ILLUMOS " +#endif +#ifdef AS_MARMALADE + "AS_MARMALADE " +#endif + + + // CPU family +#ifdef AS_PPC + "AS_PPC " +#endif +#ifdef AS_PPC_64 + "AS_PPC_64 " +#endif +#ifdef AS_X86 + "AS_X86 " +#endif +#ifdef AS_MIPS + "AS_MIPS " +#endif +#ifdef AS_SH4 + "AS_SH4 " +#endif +#ifdef AS_XENON + "AS_XENON " +#endif +#ifdef AS_ARM + "AS_ARM " +#endif +#ifdef AS_SOFTFP + "AS_SOFTFP " +#endif +#ifdef AS_X64_GCC + "AS_X64_GCC " +#endif +#ifdef AS_X64_MSVC + "AS_X64_MSVC " +#endif +#ifdef AS_SPARC + "AS_SPARC " +#endif + ; + + return string; +} + +AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version) +{ + // Verify the version that the application expects + if( (version/10000) != (ANGELSCRIPT_VERSION/10000) ) + return 0; + + if( (version/100)%100 != (ANGELSCRIPT_VERSION/100)%100 ) + return 0; + + if( (version%100) > (ANGELSCRIPT_VERSION%100) ) + return 0; + + // Verify the size of the types + asASSERT( sizeof(asBYTE) == 1 ); + asASSERT( sizeof(asWORD) == 2 ); + asASSERT( sizeof(asDWORD) == 4 ); + asASSERT( sizeof(asQWORD) == 8 ); + asASSERT( sizeof(asPWORD) == sizeof(void*) ); + + // Verify the boolean type + asASSERT( sizeof(bool) == AS_SIZEOF_BOOL ); + asASSERT( true == VALUE_OF_BOOLEAN_TRUE ); + + // Verify endianess +#ifdef AS_BIG_ENDIAN + asDWORD dw = 0x00010203; + asQWORD qw = ((asQWORD(0x00010203)<<32)|asQWORD(0x04050607)); +#else + asDWORD dw = 0x03020100; + // C++ didn't have a standard way of declaring 64bit literal constants until C++11, so + // I'm forced to do it like this to avoid compilers warnings when compiling with the full + // C++ compliance. + asQWORD qw = ((asQWORD(0x07060504)<<32)|asQWORD(0x03020100)); +#endif + asASSERT( memcmp("\x00\x01\x02\x03", &dw, 4) == 0 ); + asASSERT( memcmp("\x00\x01\x02\x03\x04\x05\x06\x07", &qw, 8) == 0 ); + UNUSED_VAR(dw); + UNUSED_VAR(qw); + + return asNEW(asCScriptEngine)(); +} + +} // extern "C" + + +// interface +int asCScriptEngine::SetEngineProperty(asEEngineProp property, asPWORD value) +{ + switch( property ) + { + case asEP_ALLOW_UNSAFE_REFERENCES: + ep.allowUnsafeReferences = value ? true : false; + break; + + case asEP_OPTIMIZE_BYTECODE: + ep.optimizeByteCode = value ? true : false; + break; + + case asEP_COPY_SCRIPT_SECTIONS: + ep.copyScriptSections = value ? true : false; + break; + + case asEP_MAX_STACK_SIZE: + if( value == 0 ) + { + // Restore default: no limit and initially size 4KB + ep.maximumContextStackSize = 0; + initialContextStackSize = 1024; + } + else + { + // The size is given in bytes, but we only store dwords + ep.maximumContextStackSize = (asUINT)value/4; + if( initialContextStackSize > ep.maximumContextStackSize ) + { + initialContextStackSize = ep.maximumContextStackSize; + if( initialContextStackSize == 0 ) + initialContextStackSize = 1; + } + } + break; + + case asEP_USE_CHARACTER_LITERALS: + ep.useCharacterLiterals = value ? true : false; + break; + + case asEP_ALLOW_MULTILINE_STRINGS: + ep.allowMultilineStrings = value ? true : false; + break; + + case asEP_ALLOW_IMPLICIT_HANDLE_TYPES: + ep.allowImplicitHandleTypes = value ? true : false; + break; + + case asEP_BUILD_WITHOUT_LINE_CUES: + ep.buildWithoutLineCues = value ? true : false; + break; + + case asEP_INIT_GLOBAL_VARS_AFTER_BUILD: + ep.initGlobalVarsAfterBuild = value ? true : false; + break; + + case asEP_REQUIRE_ENUM_SCOPE: + ep.requireEnumScope = value ? true : false; + break; + + case asEP_SCRIPT_SCANNER: + if( value <= 1 ) + ep.scanner = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_INCLUDE_JIT_INSTRUCTIONS: + ep.includeJitInstructions = value ? true : false; + break; + + case asEP_STRING_ENCODING: + if( value <= 1 ) + ep.stringEncoding = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_PROPERTY_ACCESSOR_MODE: + if( value <= 2 ) + ep.propertyAccessorMode = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_EXPAND_DEF_ARRAY_TO_TMPL: + ep.expandDefaultArrayToTemplate = value ? true : false; + break; + + case asEP_AUTO_GARBAGE_COLLECT: + ep.autoGarbageCollect = value ? true : false; + break; + + case asEP_DISALLOW_GLOBAL_VARS: + ep.disallowGlobalVars = value ? true : false; + break; + + case asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT: + ep.alwaysImplDefaultConstruct = value ? true : false; + break; + + case asEP_COMPILER_WARNINGS: + if( value <= 2 ) + ep.compilerWarnings = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE: + ep.disallowValueAssignForRefType = value ? true : false; + break; + + case asEP_ALTER_SYNTAX_NAMED_ARGS: + if( value <= 2 ) + ep.alterSyntaxNamedArgs = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_DISABLE_INTEGER_DIVISION: + ep.disableIntegerDivision = value ? true : false; + break; + + case asEP_DISALLOW_EMPTY_LIST_ELEMENTS: + ep.disallowEmptyListElements = value ? true : false; + break; + + case asEP_PRIVATE_PROP_AS_PROTECTED: + ep.privatePropAsProtected = value ? true : false; + break; + + case asEP_ALLOW_UNICODE_IDENTIFIERS: + ep.allowUnicodeIdentifiers = value ? true : false; + break; + + case asEP_HEREDOC_TRIM_MODE: + if (value <= 2) + ep.heredocTrimMode = (int)value; + else + return asINVALID_ARG; + break; + + default: + return asINVALID_ARG; + } + + return asSUCCESS; +} + +// interface +asPWORD asCScriptEngine::GetEngineProperty(asEEngineProp property) const +{ + switch( property ) + { + case asEP_ALLOW_UNSAFE_REFERENCES: + return ep.allowUnsafeReferences; + + case asEP_OPTIMIZE_BYTECODE: + return ep.optimizeByteCode; + + case asEP_COPY_SCRIPT_SECTIONS: + return ep.copyScriptSections; + + case asEP_MAX_STACK_SIZE: + return ep.maximumContextStackSize*4; + + case asEP_USE_CHARACTER_LITERALS: + return ep.useCharacterLiterals; + + case asEP_ALLOW_MULTILINE_STRINGS: + return ep.allowMultilineStrings; + + case asEP_ALLOW_IMPLICIT_HANDLE_TYPES: + return ep.allowImplicitHandleTypes; + + case asEP_BUILD_WITHOUT_LINE_CUES: + return ep.buildWithoutLineCues; + + case asEP_INIT_GLOBAL_VARS_AFTER_BUILD: + return ep.initGlobalVarsAfterBuild; + + case asEP_REQUIRE_ENUM_SCOPE: + return ep.requireEnumScope; + + case asEP_SCRIPT_SCANNER: + return ep.scanner; + + case asEP_INCLUDE_JIT_INSTRUCTIONS: + return ep.includeJitInstructions; + + case asEP_STRING_ENCODING: + return ep.stringEncoding; + + case asEP_PROPERTY_ACCESSOR_MODE: + return ep.propertyAccessorMode; + + case asEP_EXPAND_DEF_ARRAY_TO_TMPL: + return ep.expandDefaultArrayToTemplate; + + case asEP_AUTO_GARBAGE_COLLECT: + return ep.autoGarbageCollect; + + case asEP_DISALLOW_GLOBAL_VARS: + return ep.disallowGlobalVars; + + case asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT: + return ep.alwaysImplDefaultConstruct; + + case asEP_COMPILER_WARNINGS: + return ep.compilerWarnings; + + case asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE: + return ep.disallowValueAssignForRefType; + + case asEP_ALTER_SYNTAX_NAMED_ARGS: + return ep.alterSyntaxNamedArgs; + + case asEP_DISABLE_INTEGER_DIVISION: + return ep.disableIntegerDivision; + + case asEP_DISALLOW_EMPTY_LIST_ELEMENTS: + return ep.disallowEmptyListElements; + + case asEP_PRIVATE_PROP_AS_PROTECTED: + return ep.privatePropAsProtected; + + case asEP_ALLOW_UNICODE_IDENTIFIERS: + return ep.allowUnicodeIdentifiers; + + case asEP_HEREDOC_TRIM_MODE: + return ep.heredocTrimMode; + + default: + return 0; + } + + UNREACHABLE_RETURN; +} + +// interface +asIScriptFunction *asCScriptEngine::CreateDelegate(asIScriptFunction *func, void *obj) +{ + if( func == 0 || obj == 0 ) + return 0; + + // The function must be a class method + asITypeInfo *type = func->GetObjectType(); + if( type == 0 ) + return 0; + + // The object type must allow handles + if( (type->GetFlags() & asOBJ_REF) == 0 || (type->GetFlags() & (asOBJ_SCOPED | asOBJ_NOHANDLE)) ) + return 0; + + // Create the delegate the same way it would be created by the scripts + return AS_NAMESPACE_QUALIFIER CreateDelegate(reinterpret_cast(func), obj); +} + +asCScriptEngine::asCScriptEngine() +{ + asCThreadManager::Prepare(0); + + shuttingDown = false; + inDestructor = false; + + // Engine properties + { + ep.allowUnsafeReferences = false; + ep.optimizeByteCode = true; + ep.copyScriptSections = true; + ep.maximumContextStackSize = 0; // no limit + ep.useCharacterLiterals = false; + ep.allowMultilineStrings = false; + ep.allowImplicitHandleTypes = false; + // TODO: optimize: Maybe this should be turned off by default? If a debugger is not used + // then this is just slowing down the execution. + ep.buildWithoutLineCues = false; + ep.initGlobalVarsAfterBuild = true; + ep.requireEnumScope = false; + ep.scanner = 1; // utf8. 0 = ascii + ep.includeJitInstructions = false; + ep.stringEncoding = 0; // utf8. 1 = utf16 + ep.propertyAccessorMode = 2; // 0 = disable, 1 = app registered only, 2 = app and script created + ep.expandDefaultArrayToTemplate = false; + ep.autoGarbageCollect = true; + ep.disallowGlobalVars = false; + ep.alwaysImplDefaultConstruct = false; + ep.compilerWarnings = 1; // 0 = no warnings, 1 = warning, 2 = treat as error + // TODO: 3.0.0: disallowValueAssignForRefType should be true by default + ep.disallowValueAssignForRefType = false; + ep.alterSyntaxNamedArgs = 0; // 0 = no alternate syntax, 1 = accept alternate syntax but warn, 2 = accept without warning + ep.disableIntegerDivision = false; + ep.disallowEmptyListElements = false; + ep.privatePropAsProtected = false; + ep.allowUnicodeIdentifiers = false; + ep.heredocTrimMode = 1; // 0 = never trim, 1 = don't trim on single line, 2 = trim initial and final empty line + } + + gc.engine = this; + tok.engine = this; + + refCount.set(1); + stringFactory = 0; + configFailed = false; + isPrepared = false; + isBuilding = false; + deferValidationOfTemplateTypes = false; + lastModule = 0; + + + initialContextStackSize = 1024; // 4 KB (1024 * sizeof(asDWORD) + + + typeIdSeqNbr = 0; + currentGroup = &defaultGroup; + defaultAccessMask = 0xFFFFFFFF; // All bits set so that built-in functions/types will be available to all modules + + msgCallback = 0; + jitCompiler = 0; + + // Create the global namespace + defaultNamespace = AddNameSpace(""); + + requestCtxFunc = 0; + returnCtxFunc = 0; + ctxCallbackParam = 0; + + // We must set the namespace in the built-in types explicitly as + // this wasn't done by the default constructor. If we do not do + // this we will get null pointer access in other parts of the code + scriptTypeBehaviours.nameSpace = defaultNamespace; + functionBehaviours.nameSpace = defaultNamespace; + + // Reserve function id 0 for no function + scriptFunctions.PushLast(0); + + // Reserve the first typeIds for the primitive types + typeIdSeqNbr = asTYPEID_DOUBLE + 1; + + // Make sure typeId for the built-in primitives are defined according to asETypeIdFlags + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)) == asTYPEID_VOID ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)) == asTYPEID_BOOL ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)) == asTYPEID_INT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)) == asTYPEID_INT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)) == asTYPEID_INT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)) == asTYPEID_INT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)) == asTYPEID_UINT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)) == asTYPEID_UINT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)) == asTYPEID_UINT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)) == asTYPEID_UINT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)) == asTYPEID_FLOAT ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)) == asTYPEID_DOUBLE ); + + defaultArrayObjectType = 0; + + RegisterScriptObject(this); + RegisterScriptFunction(this); +} + +void asCScriptEngine::DeleteDiscardedModules() +{ + // TODO: redesign: Prevent more than one thread from entering this function at the same time. + // If a thread is already doing the work for the clean-up the other thread should + // simply return, as the first thread will continue. + + ACQUIRESHARED(engineRWLock); + asUINT maxCount = discardedModules.GetLength(); + RELEASESHARED(engineRWLock); + + for( asUINT n = 0; n < maxCount; n++ ) + { + ACQUIRESHARED(engineRWLock); + asCModule *mod = discardedModules[n]; + RELEASESHARED(engineRWLock); + + if( !mod->HasExternalReferences(shuttingDown) ) + { + asDELETE(mod, asCModule); + n--; + } + + ACQUIRESHARED(engineRWLock); + // Determine the max count again, since another module may have been discarded during the processing + maxCount = discardedModules.GetLength(); + RELEASESHARED(engineRWLock); + } + + // Go over the list of global properties, to see if it is possible to clean + // up some variables that are no longer referred to by any functions + for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + { + asCGlobalProperty *prop = globalProperties[n]; + if( prop && prop->refCount.get() == 1 ) + RemoveGlobalProperty(prop); + } +} + +asCScriptEngine::~asCScriptEngine() +{ + // TODO: clean-up: Clean up redundant code + + inDestructor = true; + + asASSERT(refCount.get() == 0); + + // If ShutDown hasn't been called yet do it now + if( !shuttingDown ) + { + AddRef(); + ShutDownAndRelease(); + } + + // Unravel the registered interface + if( defaultArrayObjectType ) + { + defaultArrayObjectType->ReleaseInternal(); + defaultArrayObjectType = 0; + } + + // Delete the functions for generated template types that may references object types + for( asUINT n = 0; n < templateInstanceTypes.GetLength(); n++ ) + { + asCObjectType *templateType = templateInstanceTypes[n]; + if( templateInstanceTypes[n] ) + templateType->DestroyInternal(); + } + for( asUINT n = 0; n < listPatternTypes.GetLength(); n++ ) + { + asCObjectType *type = listPatternTypes[n]; + if( type ) + type->ReleaseInternal(); + } + listPatternTypes.SetLength(0); + + // No script types must have survived + asASSERT( sharedScriptTypes.GetLength() == 0 ); + + // It is allowed to create new references to the engine temporarily while destroying objects + // but these references must be release immediately or else something is can go wrong later on + if( refCount.get() > 0 ) + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN); + + mapTypeIdToTypeInfo.EraseAll(); + + // First remove what is not used, so that other groups can be deleted safely + defaultGroup.RemoveConfiguration(this, true); + while( configGroups.GetLength() ) + { + // Delete config groups in the right order + asCConfigGroup *grp = configGroups.PopLast(); + if( grp ) + { + grp->RemoveConfiguration(this); + asDELETE(grp,asCConfigGroup); + } + } + // Remove what is remaining + defaultGroup.RemoveConfiguration(this); + + // Any remaining objects in templateInstanceTypes is from generated template instances + for( asUINT n = 0; n < templateInstanceTypes.GetLength(); n++ ) + { + asCObjectType *templateType = templateInstanceTypes[n]; + if( templateInstanceTypes[n] ) + templateType->ReleaseInternal(); + } + templateInstanceTypes.SetLength(0); + + asCSymbolTable::iterator it = registeredGlobalProps.List(); + for( ; it; it++ ) + { + RemoveGlobalProperty(*it); + (*it)->Release(); + } + registeredGlobalProps.Clear(); + + for( asUINT n = 0; n < templateSubTypes.GetLength(); n++ ) + { + if( templateSubTypes[n] ) + { + templateSubTypes[n]->DestroyInternal(); + templateSubTypes[n]->ReleaseInternal(); + } + } + templateSubTypes.SetLength(0); + registeredTypeDefs.SetLength(0); + registeredEnums.SetLength(0); + registeredObjTypes.SetLength(0); + + asCSymbolTable::iterator funcIt = registeredGlobalFuncs.List(); + for( ; funcIt; funcIt++ ) + (*funcIt)->ReleaseInternal(); + registeredGlobalFuncs.Clear(); + + scriptTypeBehaviours.ReleaseAllFunctions(); + functionBehaviours.ReleaseAllFunctions(); + + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + if( scriptFunctions[n] ) + { + scriptFunctions[n]->DestroyInternal(); + + // Set the engine pointer to null to signal that the function is no longer part of the engine + scriptFunctions[n]->engine = 0; + } + scriptFunctions.SetLength(0); + + // Increase the internal ref count for these builtin object types, so the destructor is not called incorrectly + scriptTypeBehaviours.AddRefInternal(); + functionBehaviours.AddRefInternal(); + + // Destroy the funcdefs + // As funcdefs are shared between modules it shouldn't be a problem to keep the objects until the engine is released + for( asUINT n = 0; n < funcDefs.GetLength(); n++ ) + if( funcDefs[n] ) + { + funcDefs[n]->DestroyInternal(); + funcDefs[n]->ReleaseInternal(); + } + funcDefs.SetLength(0); + + // Free the global properties + for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + { + asCGlobalProperty *prop = globalProperties[n]; + if( prop ) + { + asASSERT( prop->refCount.get() == 1 ); + RemoveGlobalProperty(prop); + } + } + + // Free string constants + for( asUINT n = 0; n < stringConstants.GetLength(); n++ ) + asDELETE(stringConstants[n],asCString); + stringConstants.SetLength(0); + stringToIdMap.EraseAll(); + + // Free the script section names + for( asUINT n = 0; n < scriptSectionNames.GetLength(); n++ ) + asDELETE(scriptSectionNames[n],asCString); + scriptSectionNames.SetLength(0); + + // Clean the user data + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n+1] ) + { + for( asUINT c = 0; c < cleanEngineFuncs.GetLength(); c++ ) + if( cleanEngineFuncs[c].type == userData[n] ) + cleanEngineFuncs[c].cleanFunc(this); + } + } + + // Free namespaces + for( asUINT n = 0; n < nameSpaces.GetLength(); n++ ) + asDELETE(nameSpaces[n], asSNameSpace); + nameSpaces.SetLength(0); + + asCThreadManager::Unprepare(); +} + +// interface +int asCScriptEngine::SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param) +{ + // Both callbacks or neither must be set + if( (requestCtx == 0 && returnCtx != 0) || (requestCtx != 0 && returnCtx == 0) ) + return asINVALID_ARG; + + requestCtxFunc = requestCtx; + returnCtxFunc = returnCtx; + ctxCallbackParam = param; + + return 0; +} + +// interface +asIScriptContext *asCScriptEngine::RequestContext() +{ + if( requestCtxFunc ) + { + // The return callback must also exist + asASSERT( returnCtxFunc ); + + asIScriptContext *ctx = requestCtxFunc(this, ctxCallbackParam); + return ctx; + } + + // As fallback we create a new context + return CreateContext(); +} + +// internal +asCModule *asCScriptEngine::FindNewOwnerForSharedType(asCTypeInfo *in_type, asCModule *in_mod) +{ + asASSERT( in_type->IsShared() ); + + if( in_type->module != in_mod) + return in_type->module; + + for( asUINT n = 0; n < scriptModules.GetLength(); n++ ) + { + // TODO: optimize: If the modules already stored the shared types separately, this would be quicker + int foundIdx = -1; + asCModule *mod = scriptModules[n]; + if( mod == in_type->module ) continue; + if( in_type->flags & asOBJ_ENUM ) + foundIdx = mod->enumTypes.IndexOf(CastToEnumType(in_type)); + else if (in_type->flags & asOBJ_TYPEDEF) + foundIdx = mod->typeDefs.IndexOf(CastToTypedefType(in_type)); + else if (in_type->flags & asOBJ_FUNCDEF) + foundIdx = mod->funcDefs.IndexOf(CastToFuncdefType(in_type)); + else + foundIdx = mod->classTypes.IndexOf(CastToObjectType(in_type)); + + if( foundIdx >= 0 ) + { + in_type->module = mod; + break; + } + } + + return in_type->module; +} + +// internal +asCModule *asCScriptEngine::FindNewOwnerForSharedFunc(asCScriptFunction *in_func, asCModule *in_mod) +{ + asASSERT( in_func->IsShared() ); + asASSERT(!(in_func->funcType & asFUNC_FUNCDEF)); + + if( in_func->module != in_mod) + return in_func->module; + + for( asUINT n = 0; n < scriptModules.GetLength(); n++ ) + { + // TODO: optimize: If the modules already stored the shared types separately, this would be quicker + int foundIdx = -1; + asCModule *mod = scriptModules[n]; + if( mod == in_func->module ) continue; + foundIdx = mod->scriptFunctions.IndexOf(in_func); + + if( foundIdx >= 0 ) + { + in_func->module = mod; + break; + } + } + + return in_func->module; +} + +// interface +void asCScriptEngine::ReturnContext(asIScriptContext *ctx) +{ + if( returnCtxFunc ) + { + returnCtxFunc(this, ctx, ctxCallbackParam); + return; + } + + // As fallback we just release the context + if( ctx ) + ctx->Release(); +} + +// interface +int asCScriptEngine::AddRef() const +{ + asASSERT( refCount.get() > 0 || inDestructor ); + return refCount.atomicInc(); +} + +// interface +int asCScriptEngine::Release() const +{ + int r = refCount.atomicDec(); + + if( r == 0 ) + { + // It is possible that some function will temporarily increment the engine ref count + // during clean-up for example while destroying the objects in the garbage collector. + if( !inDestructor ) + asDELETE(const_cast(this),asCScriptEngine); + return 0; + } + + return r; +} + +// interface +int asCScriptEngine::ShutDownAndRelease() +{ + // Do a full garbage collection cycle to clean up any object that may still hold on to the engine + GarbageCollect(); + + // Set the flag that the engine is being shutdown now. This will speed up + // the process, and will also allow the engine to warn about invalid calls + shuttingDown = true; + + // Clear the context callbacks. If new context's are needed for the clean-up the engine will take care of this itself. + // Context callbacks are normally used for pooling contexts, and if we allow new contexts to be created without being + // immediately destroyed afterwards it means the engine's refcount will increase. This is turn may cause memory access + // violations later on when the pool releases its contexts. + SetContextCallbacks(0, 0, 0); + + // The modules must be deleted first, as they may use + // object types from the config groups + for( asUINT n = (asUINT)scriptModules.GetLength(); n-- > 0; ) + if( scriptModules[n] ) + scriptModules[n]->Discard(); + scriptModules.SetLength(0); + + // Do another full garbage collection to destroy the object types/functions + // that may have been placed in the gc when destroying the modules + GarbageCollect(); + + // Do another sweep to delete discarded modules, that may not have + // been deleted earlier due to still having external references + DeleteDiscardedModules(); + + // If the application hasn't registered GC behaviours for all types + // that can form circular references with script types, then there + // may still be objects in the GC. + gc.ReportAndReleaseUndestroyedObjects(); + + // Release the engine reference + return Release(); +} + +// internal +asSNameSpace *asCScriptEngine::AddNameSpace(const char *name) +{ + // First check if it doesn't exist already + asSNameSpace *ns = FindNameSpace(name); + if( ns ) return ns; + + ns = asNEW(asSNameSpace); + if( ns == 0 ) + { + // Out of memory + return 0; + } + ns->name = name; + + nameSpaces.PushLast(ns); + + return ns; +} + +// internal +asSNameSpace *asCScriptEngine::FindNameSpace(const char *name) const +{ + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < nameSpaces.GetLength(); n++ ) + if( nameSpaces[n]->name == name ) + return nameSpaces[n]; + + return 0; +} + +// interface +const char *asCScriptEngine::GetDefaultNamespace() const +{ + return defaultNamespace->name.AddressOf(); +} + +// interface +int asCScriptEngine::SetDefaultNamespace(const char *nameSpace) +{ + if( nameSpace == 0 ) + return ConfigError(asINVALID_ARG, "SetDefaultNamespace", nameSpace, 0); + + asCString ns = nameSpace; + if( ns != "" ) + { + // Make sure the namespace is composed of alternating identifier and :: + size_t pos = 0; + bool expectIdentifier = true; + size_t len; + eTokenType t = ttIdentifier; + + for( ; pos < ns.GetLength(); pos += len) + { + t = tok.GetToken(ns.AddressOf() + pos, ns.GetLength() - pos, &len); + if( (expectIdentifier && t != ttIdentifier) || (!expectIdentifier && t != ttScope) ) + return ConfigError(asINVALID_DECLARATION, "SetDefaultNamespace", nameSpace, 0); + + expectIdentifier = !expectIdentifier; + } + + // If the namespace ends with :: then strip it off + if( t == ttScope ) + ns.SetLength(ns.GetLength()-2); + } + + defaultNamespace = AddNameSpace(ns.AddressOf()); + + return 0; +} + +// interface +void *asCScriptEngine::SetUserData(void *data, asPWORD type) +{ + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *oldData = reinterpret_cast(userData[n+1]); + userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engineRWLock); + + return 0; +} + +// interface +void *asCScriptEngine::GetUserData(asPWORD type) const +{ + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engineRWLock); + + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + RELEASESHARED(engineRWLock); + return reinterpret_cast(userData[n+1]); + } + } + + RELEASESHARED(engineRWLock); + + return 0; +} + +// interface +int asCScriptEngine::SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) +{ + msgCallback = true; + msgCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) + { + msgCallback = false; + return asNOT_SUPPORTED; + } + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + msgCallback = false; + return asINVALID_ARG; + } + } + int r = DetectCallingConvention(isObj, callback, callConv, 0, &msgCallbackFunc); + if( r < 0 ) msgCallback = false; + return r; +} + +// interface +int asCScriptEngine::ClearMessageCallback() +{ + msgCallback = false; + return 0; +} + +// interface +int asCScriptEngine::WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) +{ + // Validate input parameters + if( section == 0 || + message == 0 ) + return asINVALID_ARG; + + // If there is no callback then there's nothing to do + if( !msgCallback ) + return 0; + + // If a pre-message has been set, then write that first + if( preMessage.isSet ) + { + asSMessageInfo msg; + msg.section = preMessage.scriptname.AddressOf(); + msg.row = preMessage.r; + msg.col = preMessage.c; + msg.type = asMSGTYPE_INFORMATION; + msg.message = preMessage.message.AddressOf(); + + if( msgCallbackFunc.callConv < ICC_THISCALL ) + CallGlobalFunction(&msg, msgCallbackObj, &msgCallbackFunc, 0); + else + CallObjectMethod(msgCallbackObj, &msg, &msgCallbackFunc, 0); + + preMessage.isSet = false; + } + + // Write the message to the callback + asSMessageInfo msg; + msg.section = section; + msg.row = row; + msg.col = col; + msg.type = type; + msg.message = message; + + if( msgCallbackFunc.callConv < ICC_THISCALL ) + CallGlobalFunction(&msg, msgCallbackObj, &msgCallbackFunc, 0); + else + CallObjectMethod(msgCallbackObj, &msg, &msgCallbackFunc, 0); + + return 0; +} + +int asCScriptEngine::SetJITCompiler(asIJITCompiler *compiler) +{ + jitCompiler = compiler; + return asSUCCESS; +} + +asIJITCompiler *asCScriptEngine::GetJITCompiler() const +{ + return jitCompiler; +} + +// interface +asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLength, asUINT *tokenLength) const +{ + if( stringLength == 0 ) + stringLength = strlen(string); + + size_t len; + asETokenClass tc; + tok.GetToken(string, stringLength, &len, &tc); + + if( tokenLength ) + *tokenLength = (asUINT)len; + + return tc; +} + +// interface +asIScriptModule *asCScriptEngine::GetModule(const char *module, asEGMFlags flag) +{ + asCModule *mod = GetModule(module, false); + + if( flag == asGM_ALWAYS_CREATE ) + { + if( mod != 0 ) + mod->Discard(); + + return GetModule(module, true); + } + + if( mod == 0 && flag == asGM_CREATE_IF_NOT_EXISTS ) + return GetModule(module, true); + + return mod; +} + +// interface +int asCScriptEngine::DiscardModule(const char *module) +{ + asCModule *mod = GetModule(module, false); + if( mod == 0 ) return asNO_MODULE; + + mod->Discard(); + + return 0; +} + +// interface +asUINT asCScriptEngine::GetModuleCount() const +{ + ACQUIRESHARED(engineRWLock); + asUINT length = asUINT(scriptModules.GetLength()); + RELEASESHARED(engineRWLock); + return length; +} + +// interface +asIScriptModule *asCScriptEngine::GetModuleByIndex(asUINT index) const +{ + asIScriptModule *mod = 0; + ACQUIRESHARED(engineRWLock); + if( index < scriptModules.GetLength() ) + mod = scriptModules[index]; + RELEASESHARED(engineRWLock); + return mod; +} + +// internal +int asCScriptEngine::GetFactoryIdByDecl(const asCObjectType *ot, const char *decl) +{ + asCModule *mod = 0; + + // Is this a script class? + if( (ot->flags & asOBJ_SCRIPT_OBJECT) && ot->size > 0 ) + mod = scriptFunctions[ot->beh.factories[0]]->module; + + asCBuilder bld(this, mod); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCScriptFunction func(this, mod, asFUNC_DUMMY); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false, 0, 0, defaultNamespace); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Search for matching factory function + int id = -1; + for( asUINT n = 0; n < ot->beh.factories.GetLength(); n++ ) + { + asCScriptFunction *f = scriptFunctions[ot->beh.factories[n]]; + if( f->IsSignatureEqual(&func) ) + { + id = ot->beh.factories[n]; + break; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + + +// internal +int asCScriptEngine::GetMethodIdByDecl(const asCObjectType *ot, const char *decl, asCModule *mod) +{ + asCBuilder bld(this, mod); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCScriptFunction func(this, mod, asFUNC_DUMMY); + + // Set the object type so that the signature can be properly compared + // This cast is OK, it will only be used for comparison + func.objectType = const_cast(ot); + func.objectType->AddRefInternal(); + + int r = bld.ParseFunctionDeclaration(func.objectType, decl, &func, false); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Search script functions for matching interface + int id = -1; + for( asUINT n = 0; n < ot->methods.GetLength(); ++n ) + { + if( func.IsSignatureEqual(scriptFunctions[ot->methods[n]]) ) + { + if( id == -1 ) + id = ot->methods[n]; + else + return asMULTIPLE_FUNCTIONS; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + + +// internal +asCString asCScriptEngine::GetFunctionDeclaration(int funcId) +{ + asCString str; + asCScriptFunction *func = GetScriptFunction(funcId); + if( func ) + str = func->GetDeclarationStr(); + + return str; +} + +// internal +asCScriptFunction *asCScriptEngine::GetScriptFunction(int funcId) const +{ + if( funcId < 0 || funcId >= (int)scriptFunctions.GetLength() ) + return 0; + + return scriptFunctions[funcId]; +} + + +// interface +asIScriptContext *asCScriptEngine::CreateContext() +{ + asIScriptContext *ctx = 0; + CreateContext(&ctx, false); + return ctx; +} + +// internal +int asCScriptEngine::CreateContext(asIScriptContext **context, bool isInternal) +{ + *context = asNEW(asCContext)(this, !isInternal); + if( *context == 0 ) + return asOUT_OF_MEMORY; + + // We need to make sure the engine has been + // prepared before any context is executed + PrepareEngine(); + + return 0; +} + +// interface +int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) +{ + int r; + asCDataType dt; + asCBuilder bld(this, 0); + r = bld.ParseDataType(obj, &dt, defaultNamespace); + if( r < 0 ) + return ConfigError(r, "RegisterObjectProperty", obj, declaration); + + if (dt.GetTypeInfo() == 0 || (dt.IsObjectHandle() && !(dt.GetTypeInfo()->GetFlags() & asOBJ_IMPLICIT_HANDLE))) + return ConfigError(asINVALID_OBJECT, "RegisterObjectProperty", obj, declaration); + + // Don't allow modifying generated template instances + if( dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(CastToObjectType(dt.GetTypeInfo())) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectProperty", obj, declaration); + + // Verify that the correct config group is used + if( currentGroup->FindType(dt.GetTypeInfo()->name.AddressOf()) == 0 ) + return ConfigError(asWRONG_CONFIG_GROUP, "RegisterObjectProperty", obj, declaration); + + asCDataType type; + asCString name; + + if( (r = bld.VerifyProperty(&dt, declaration, name, type, 0)) < 0 ) + return ConfigError(r, "RegisterObjectProperty", obj, declaration); + + // The VM currently only supports 16bit offsets + // TODO: The VM needs to have support for 32bit offsets. Probably with a second ADDSi instruction + // However, when implementing this it is necessary for the bytecode serialization to support + // the switch between the instructions upon loading bytecode as the offset may not be the + // same on all platforms + if( byteOffset > 32767 || byteOffset < -32768 ) + return ConfigError(asINVALID_ARG, "RegisterObjectProperty", obj, declaration); + + asCObjectProperty *prop = asNEW(asCObjectProperty); + if( prop == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectProperty", obj, declaration); + + prop->name = name; + prop->type = type; + prop->byteOffset = byteOffset; + prop->isPrivate = false; + prop->isProtected = false; + prop->accessMask = defaultAccessMask; + + CastToObjectType(dt.GetTypeInfo())->properties.PushLast(prop); + + // Add references to types so they are not released too early + if( type.GetTypeInfo() ) + { + type.GetTypeInfo()->AddRefInternal(); + + // Add template instances to the config group + if( (type.GetTypeInfo()->flags & asOBJ_TEMPLATE) && !currentGroup->types.Exists(type.GetTypeInfo()) ) + currentGroup->types.PushLast(type.GetTypeInfo()); + } + + currentGroup->AddReferencesForType(this, type.GetTypeInfo()); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterInterface(const char *name) +{ + if( name == 0 ) return ConfigError(asINVALID_NAME, "RegisterInterface", 0, 0); + + // Verify if the name has been registered as a type already + if( GetRegisteredType(name, defaultNamespace) ) + return asALREADY_REGISTERED; + + // Use builder to parse the datatype + asCDataType dt; + asCBuilder bld(this, 0); + bool oldMsgCallback = msgCallback; msgCallback = false; + int r = bld.ParseDataType(name, &dt, defaultNamespace); + msgCallback = oldMsgCallback; + if( r >= 0 ) + { + // If it is not in the defaultNamespace then the type was successfully parsed because + // it is declared in a parent namespace which shouldn't be treated as an error + if( dt.GetTypeInfo() && dt.GetTypeInfo()->nameSpace == defaultNamespace ) + return ConfigError(asERROR, "RegisterInterface", name, 0); + } + + // Make sure the name is not a reserved keyword + size_t tokenLen; + int token = tok.GetToken(name, strlen(name), &tokenLen); + if( token != ttIdentifier || strlen(name) != tokenLen ) + return ConfigError(asINVALID_NAME, "RegisterInterface", name, 0); + + r = bld.CheckNameConflict(name, 0, 0, defaultNamespace); + if( r < 0 ) + return ConfigError(asNAME_TAKEN, "RegisterInterface", name, 0); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Register the object type for the interface + asCObjectType *st = asNEW(asCObjectType)(this); + if( st == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterInterface", name, 0); + + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT | asOBJ_SHARED; + st->size = 0; // Cannot be instantiated + st->name = name; + st->nameSpace = defaultNamespace; + + // Use the default script class behaviours + st->beh.factory = 0; + st->beh.addref = scriptTypeBehaviours.beh.addref; + scriptFunctions[st->beh.addref]->AddRefInternal(); + st->beh.release = scriptTypeBehaviours.beh.release; + scriptFunctions[st->beh.release]->AddRefInternal(); + st->beh.copy = 0; + + allRegisteredTypes.Insert(asSNameSpaceNamePair(st->nameSpace, st->name), st); + registeredObjTypes.PushLast(st); + + currentGroup->types.PushLast(st); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *declaration) +{ + // Verify that the correct config group is set. + if( currentGroup->FindType(intf) == 0 ) + return ConfigError(asWRONG_CONFIG_GROUP, "RegisterInterfaceMethod", intf, declaration); + + asCDataType dt; + asCBuilder bld(this, 0); + int r = bld.ParseDataType(intf, &dt, defaultNamespace); + if( r < 0 ) + return ConfigError(r, "RegisterInterfaceMethod", intf, declaration); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_INTERFACE); + if( func == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterInterfaceMethod", intf, declaration); + + func->objectType = CastToObjectType(dt.GetTypeInfo()); + func->objectType->AddRefInternal(); + + r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, false); + if( r < 0 ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION, "RegisterInterfaceMethod", intf, declaration); + } + + // Check name conflicts + r = bld.CheckNameConflictMember(dt.GetTypeInfo(), func->name.AddressOf(), 0, 0, false); + if( r < 0 ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN, "RegisterInterfaceMethod", intf, declaration); + } + + func->id = GetNextScriptFunctionId(); + AddScriptFunction(func); + + // The index into the interface's vftable chunk should be + // its index in the methods array. + func->vfTableIdx = int(func->objectType->methods.GetLength()); + + func->objectType->methods.PushLast(func->id); + + func->ComputeSignatureId(); + + currentGroup->AddReferencesForFunc(this, func); + + // Return function id as success + return func->id; +} + +int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD flags) +{ + int r; + + isPrepared = false; + + // Verify flags + // Must have either asOBJ_REF or asOBJ_VALUE + if( flags & asOBJ_REF ) + { + // Can optionally have the asOBJ_GC, asOBJ_NOHANDLE, asOBJ_SCOPED, or asOBJ_TEMPLATE flag set, but nothing else + if( flags & ~(asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE | asOBJ_NOCOUNT | asOBJ_IMPLICIT_HANDLE) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // flags are exclusive + if( (flags & asOBJ_GC) && (flags & (asOBJ_NOHANDLE|asOBJ_SCOPED|asOBJ_NOCOUNT)) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + if( (flags & asOBJ_NOHANDLE) && (flags & (asOBJ_GC|asOBJ_SCOPED|asOBJ_NOCOUNT|asOBJ_IMPLICIT_HANDLE)) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + if( (flags & asOBJ_SCOPED) && (flags & (asOBJ_GC|asOBJ_NOHANDLE|asOBJ_NOCOUNT|asOBJ_IMPLICIT_HANDLE)) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + if( (flags & asOBJ_NOCOUNT) && (flags & (asOBJ_GC|asOBJ_NOHANDLE|asOBJ_SCOPED)) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // Implicit handle is only allowed if the engine property for this is turned on + if( !ep.allowImplicitHandleTypes && (flags & asOBJ_IMPLICIT_HANDLE) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + else if( flags & asOBJ_VALUE ) + { + // Cannot use reference flags + if( flags & (asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_NOCOUNT | asOBJ_IMPLICIT_HANDLE) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // Flags are exclusive + if( (flags & asOBJ_POD) && (flags & (asOBJ_ASHANDLE | asOBJ_TEMPLATE)) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // If the app type is given, we must validate the flags + if( flags & asOBJ_APP_CLASS ) + { + // Must not set the primitive or float flag + if( flags & (asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + else + { + // Must not set the class properties, without the class flag + if( flags & (asOBJ_APP_CLASS_CONSTRUCTOR | + asOBJ_APP_CLASS_DESTRUCTOR | + asOBJ_APP_CLASS_ASSIGNMENT | + asOBJ_APP_CLASS_COPY_CONSTRUCTOR | + asOBJ_APP_CLASS_ALLINTS | + asOBJ_APP_CLASS_ALLFLOATS) ) + { + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + } + + if( flags & asOBJ_APP_PRIMITIVE ) + { + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_FLOAT | + asOBJ_APP_ARRAY) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + else if( flags & asOBJ_APP_FLOAT ) + { + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_PRIMITIVE | + asOBJ_APP_ARRAY) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + else if( flags & asOBJ_APP_ARRAY ) + { + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_PRIMITIVE | + asOBJ_APP_FLOAT) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + } + else + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // Don't allow anything else than the defined flags +#ifndef WIP_16BYTE_ALIGN + if( flags - (flags & asOBJ_MASK_VALID_FLAGS) ) +#else + if( flags - (flags & (asOBJ_MASK_VALID_FLAGS | asOBJ_APP_ALIGN16)) ) +#endif + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // Value types must have a defined size + if( (flags & asOBJ_VALUE) && byteSize == 0 ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_VALUE_TYPE_MUST_HAVE_SIZE); + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + + // Verify type name + if( name == 0 ) + return ConfigError(asINVALID_NAME, "RegisterObjectType", name, 0); + + asCString typeName; + asCBuilder bld(this, 0); + if( flags & asOBJ_TEMPLATE ) + { + asCArray subtypeNames; + r = bld.ParseTemplateDecl(name, &typeName, subtypeNames); + if( r < 0 ) + return ConfigError(r, "RegisterObjectType", name, 0); + + // Verify that the template name hasn't been registered as a type already + if( GetRegisteredType(typeName, defaultNamespace) ) + // This is not an irrepairable error, as it may just be that the same type is registered twice + return asALREADY_REGISTERED; + + asCObjectType *type = asNEW(asCObjectType)(this); + if( type == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectType", name, 0); + + type->name = typeName; + type->nameSpace = defaultNamespace; + type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif + type->flags = flags; + type->accessMask = defaultAccessMask; + + // Store it in the object types + allRegisteredTypes.Insert(asSNameSpaceNamePair(type->nameSpace, type->name), type); + currentGroup->types.PushLast(type); + registeredObjTypes.PushLast(type); + registeredTemplateTypes.PushLast(type); + + // Define the template subtypes + for( asUINT subTypeIdx = 0; subTypeIdx < subtypeNames.GetLength(); subTypeIdx++ ) + { + asCTypeInfo *subtype = 0; + for( asUINT n = 0; n < templateSubTypes.GetLength(); n++ ) + { + if( templateSubTypes[n]->name == subtypeNames[subTypeIdx] ) + { + subtype = templateSubTypes[n]; + break; + } + } + if( subtype == 0 ) + { + // Create the new subtype if not already existing + subtype = asNEW(asCTypeInfo)(this); + if( subtype == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectType", name, 0); + + subtype->name = subtypeNames[subTypeIdx]; + subtype->size = 0; + subtype->flags = asOBJ_TEMPLATE_SUBTYPE; + templateSubTypes.PushLast(subtype); + } + type->templateSubTypes.PushLast(asCDataType::CreateType(subtype, false)); + subtype->AddRefInternal(); + } + } + else + { + typeName = name; + + // Verify if the name has been registered as a type already + if( GetRegisteredType(typeName, defaultNamespace) ) + // This is not an irrepairable error, as it may just be that the same type is registered twice + return asALREADY_REGISTERED; + + // TODO: clean up: Is it really necessary to check here? + for( asUINT n = 0; n < templateInstanceTypes.GetLength(); n++ ) + { + if( templateInstanceTypes[n] && + templateInstanceTypes[n]->name == typeName && + templateInstanceTypes[n]->nameSpace == defaultNamespace ) + // This is not an irrepairable error, as it may just be that the same type is registered twice + return asALREADY_REGISTERED; + } + + // Keep the most recent template generated instance type, so we know what it was before parsing the datatype + asCObjectType *mostRecentTemplateInstanceType = 0; + asUINT originalSizeOfGeneratedTemplateTypes = (asUINT)generatedTemplateTypes.GetLength(); + if( originalSizeOfGeneratedTemplateTypes ) + mostRecentTemplateInstanceType = generatedTemplateTypes[originalSizeOfGeneratedTemplateTypes-1]; + + // Use builder to parse the datatype + asCDataType dt; + bool oldMsgCallback = msgCallback; msgCallback = false; + r = bld.ParseDataType(name, &dt, defaultNamespace); + msgCallback = oldMsgCallback; + + // If the builder fails or the namespace is different than the default + // namespace, then the type name is new and it should be registered + if( r < 0 || dt.GetTypeInfo()->nameSpace != defaultNamespace ) + { + // Make sure the name is not a reserved keyword + size_t tokenLen; + int token = tok.GetToken(name, typeName.GetLength(), &tokenLen); + if( token != ttIdentifier || typeName.GetLength() != tokenLen ) + return ConfigError(asINVALID_NAME, "RegisterObjectType", name, 0); + + r = bld.CheckNameConflict(name, 0, 0, defaultNamespace); + if( r < 0 ) + return ConfigError(asNAME_TAKEN, "RegisterObjectType", name, 0); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Put the data type in the list + asCObjectType *type = asNEW(asCObjectType)(this); + if( type == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectType", name, 0); + + type->name = typeName; + type->nameSpace = defaultNamespace; + type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif + type->flags = flags; + type->accessMask = defaultAccessMask; + + allRegisteredTypes.Insert(asSNameSpaceNamePair(type->nameSpace, type->name), type); + registeredObjTypes.PushLast(type); + + currentGroup->types.PushLast(type); + } + else + { + // The application is registering a template specialization so we + // need to replace the template instance type with the new type. + + // TODO: Template: We don't require the lower dimensions to be registered first for registered template types + // int[][] must not be allowed to be registered + // if int[] hasn't been registered first + if( dt.GetSubType().IsTemplate() ) + return ConfigError(asLOWER_ARRAY_DIMENSION_NOT_REGISTERED, "RegisterObjectType", name, 0); + + if( dt.IsReadOnly() || + dt.IsReference() ) + return ConfigError(asINVALID_TYPE, "RegisterObjectType", name, 0); + + // Was the template instance type generated before? + if( generatedTemplateTypes.Exists(CastToObjectType(dt.GetTypeInfo())) && + generatedTemplateTypes[generatedTemplateTypes.GetLength()-1] == mostRecentTemplateInstanceType ) + { + asCString str; + str.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, typeName.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return ConfigError(asNOT_SUPPORTED, "RegisterObjectType", name, 0); + } + + // If this is not a generated template instance type, then it means it is an + // already registered template specialization + if( !generatedTemplateTypes.Exists(CastToObjectType(dt.GetTypeInfo())) ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectType", name, 0); + + // TODO: Add this again. The type is used by the factory stubs so we need to discount that + // Is the template instance type already being used? +// if( dt.GetTypeInfo()->GetRefCount() > 1 ) +// return ConfigError(asNOT_SUPPORTED, "RegisterObjectType", name, 0); + + // Put the data type in the list + asCObjectType *type = asNEW(asCObjectType)(this); + if( type == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectType", name, 0); + + type->name = dt.GetTypeInfo()->name; + // The namespace will be the same as the original template type + type->nameSpace = dt.GetTypeInfo()->nameSpace; + type->templateSubTypes.PushLast(dt.GetSubType()); + for( asUINT s = 0; s < type->templateSubTypes.GetLength(); s++ ) + if( type->templateSubTypes[s].GetTypeInfo() ) + type->templateSubTypes[s].GetTypeInfo()->AddRefInternal(); + type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif + type->flags = flags; + type->accessMask = defaultAccessMask; + + templateInstanceTypes.PushLast(type); + + currentGroup->types.PushLast(type); + + // Remove the template instance type, which will no longer be used. + // It is possible that multiple template instances are generated if + // they have any relationship, so all of them must be removed + while( generatedTemplateTypes.GetLength() > originalSizeOfGeneratedTemplateTypes ) + RemoveTemplateInstanceType(generatedTemplateTypes[generatedTemplateTypes.GetLength()-1]); + } + } + + // Return the type id as the success (except for template types) + if( flags & asOBJ_TEMPLATE ) + return asSUCCESS; + + return GetTypeIdByDecl(name); +} + +// interface +int asCScriptEngine::RegisterObjectBehaviour(const char *datatype, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ + if( datatype == 0 ) return ConfigError(asINVALID_ARG, "RegisterObjectBehaviour", datatype, decl); + + // Determine the object type + asCBuilder bld(this, 0); + asCDataType type; + int r = bld.ParseDataType(datatype, &type, defaultNamespace); + if( r < 0 ) + return ConfigError(r, "RegisterObjectBehaviour", datatype, decl); + + if( type.GetTypeInfo() == 0 || (type.IsObjectHandle() && !(type.GetTypeInfo()->GetFlags() & asOBJ_IMPLICIT_HANDLE)) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + + // Don't allow application to modify built-in types + if( type.GetTypeInfo() == &functionBehaviours || + type.GetTypeInfo() == &scriptTypeBehaviours ) + return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + + if( type.IsReadOnly() || type.IsReference() ) + return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + + // Don't allow modifying generated template instances + if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(CastToObjectType(type.GetTypeInfo())) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + + return RegisterBehaviourToObjectType(CastToObjectType(type.GetTypeInfo()), behaviour, decl, funcPointer, callConv, auxiliary); +} + +// internal +int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); +#endif + + asSSystemFunctionInterface internal; + bool isMethod = !(behaviour == asBEHAVE_FACTORY || + behaviour == asBEHAVE_LIST_FACTORY || + behaviour == asBEHAVE_TEMPLATE_CALLBACK); + int r = DetectCallingConvention(isMethod, funcPointer, callConv, auxiliary, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // TODO: cleanup: This is identical to what is in RegisterMethodToObjectType + // If the object type is a template, make sure there are no generated instances already + if( objectType->flags & asOBJ_TEMPLATE ) + { + for( asUINT n = 0; n < generatedTemplateTypes.GetLength(); n++ ) + { + asCObjectType *tmpl = generatedTemplateTypes[n]; + if( tmpl->name == objectType->name && + tmpl->nameSpace == objectType->nameSpace && + !(tmpl->templateSubTypes[0].GetTypeInfo() && (tmpl->templateSubTypes[0].GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE)) ) + { + asCString msg; + msg.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, asCDataType::CreateType(tmpl, false).Format(tmpl->nameSpace).AddressOf()); + WriteMessage("",0,0, asMSGTYPE_ERROR, msg.AddressOf()); + return ConfigError(asERROR, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } + } + + isPrepared = false; + + asSTypeBehaviour *beh = &objectType->beh; + + // Verify function declaration + asCScriptFunction func(this, 0, asFUNC_DUMMY); + + bool expectListPattern = behaviour == asBEHAVE_LIST_FACTORY || behaviour == asBEHAVE_LIST_CONSTRUCT; + asCScriptNode *listPattern = 0; + asCBuilder bld(this, 0); + r = bld.ParseFunctionDeclaration(objectType, decl, &func, true, &internal.paramAutoHandles, &internal.returnAutoHandle, 0, expectListPattern ? &listPattern : 0); + if( r < 0 ) + { + if( listPattern ) + listPattern->Destroy(this); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + func.name.Format("$beh%d", behaviour); + + if( behaviour != asBEHAVE_FACTORY && behaviour != asBEHAVE_LIST_FACTORY ) + { + func.objectType = objectType; + func.objectType->AddRefInternal(); + } + + // Check if the method restricts that use of the template to value types or reference types + if( objectType->flags & asOBJ_TEMPLATE ) + { + r = SetTemplateRestrictions(objectType, &func, "RegisterObjectBehaviour", decl); + if (r < 0) + return r; + } + + if( behaviour == asBEHAVE_CONSTRUCT ) + { + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( objectType->flags & asOBJ_SCRIPT_OBJECT ) + { + // The script object is a special case + asASSERT(func.parameterTypes.GetLength() == 1); + + beh->construct = AddBehaviourFunction(func, internal); + beh->factory = beh->construct; + scriptFunctions[beh->factory]->AddRefInternal(); + beh->constructors.PushLast(beh->construct); + beh->factories.PushLast(beh->factory); + func.id = beh->construct; + } + else + { + // Verify that it is a value type + if( !(func.objectType->flags & asOBJ_VALUE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // The templates take a hidden parameter with the object type + if( (objectType->flags & asOBJ_TEMPLATE) && + (func.parameterTypes.GetLength() == 0 || + !func.parameterTypes[0].IsReference()) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_FIRST_PARAM_MUST_BE_REF_FOR_TEMPLATE_FACTORY); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // TODO: Verify that the same constructor hasn't been registered already + + // Store all constructors in a list + func.id = AddBehaviourFunction(func, internal); + beh->constructors.PushLast(func.id); + if( func.parameterTypes.GetLength() == 0 || + (func.parameterTypes.GetLength() == 1 && (objectType->flags & asOBJ_TEMPLATE)) ) + { + beh->construct = func.id; + } + else if( func.parameterTypes.GetLength() == 1 ) + { + // Is this the copy constructor? + asCDataType paramType = func.parameterTypes[0]; + + // If the parameter is object, and const reference for input or inout, + // and same type as this class, then this is a copy constructor. + if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && + (func.inOutFlags[0] & asTM_INREF) && paramType.GetTypeInfo() == objectType ) + beh->copyconstruct = func.id; + } + } + } + else if( behaviour == asBEHAVE_DESTRUCT ) + { + // Must be a value type + if( !(func.objectType->flags & asOBJ_VALUE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( beh->destruct ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + func.id = beh->destruct = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_LIST_CONSTRUCT ) + { + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + { + if( listPattern ) + listPattern->Destroy(this); + + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Verify that it is a value type + if( !(func.objectType->flags & asOBJ_VALUE) ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Verify the parameters + if( func.parameterTypes.GetLength() != 1 || !func.parameterTypes[0].IsReference() ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Don't accept duplicates + if( beh->listFactory ) + { + if( listPattern ) + listPattern->Destroy(this); + + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Add the function + func.id = AddBehaviourFunction(func, internal); + + // Re-use the listFactory member, as it is not possible to have both anyway + beh->listFactory = func.id; + + // Store the list pattern for this function + r = scriptFunctions[func.id]->RegisterListPattern(decl, listPattern); + + if( listPattern ) + listPattern->Destroy(this); + + if( r < 0 ) + return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + else if( behaviour == asBEHAVE_FACTORY || behaviour == asBEHAVE_LIST_FACTORY ) + { + // Must be a ref type and must not have asOBJ_NOHANDLE + if( !(objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_NOHANDLE) ) + { + if( listPattern ) + listPattern->Destroy(this); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Verify that the return type is a handle to the type + if( func.returnType != asCDataType::CreateObjectHandle(objectType, false) ) + { + if( listPattern ) + listPattern->Destroy(this); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // The templates take a hidden parameter with the object type + if( (objectType->flags & asOBJ_TEMPLATE) && + (func.parameterTypes.GetLength() == 0 || + !func.parameterTypes[0].IsReference()) ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_FIRST_PARAM_MUST_BE_REF_FOR_TEMPLATE_FACTORY); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( behaviour == asBEHAVE_LIST_FACTORY ) + { + // Make sure the factory takes a reference as its last parameter + if( objectType->flags & asOBJ_TEMPLATE ) + { + if( func.parameterTypes.GetLength() != 2 || !func.parameterTypes[1].IsReference() ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_TEMPLATE_LIST_FACTORY_EXPECTS_2_REF_PARAMS); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } + else + { + if( func.parameterTypes.GetLength() != 1 || !func.parameterTypes[0].IsReference() ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } + } + + // TODO: Verify that the same factory function hasn't been registered already + + // Don't accept duplicates + if( behaviour == asBEHAVE_LIST_FACTORY && beh->listFactory ) + { + if( listPattern ) + listPattern->Destroy(this); + + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Store all factory functions in a list + func.id = AddBehaviourFunction(func, internal); + + // The list factory is a special factory and isn't stored together with the rest + if( behaviour != asBEHAVE_LIST_FACTORY ) + beh->factories.PushLast(func.id); + + if( (func.parameterTypes.GetLength() == 0) || + (func.parameterTypes.GetLength() == 1 && (objectType->flags & asOBJ_TEMPLATE)) ) + { + beh->factory = func.id; + } + else if( (func.parameterTypes.GetLength() == 1) || + (func.parameterTypes.GetLength() == 2 && (objectType->flags & asOBJ_TEMPLATE)) ) + { + if( behaviour == asBEHAVE_LIST_FACTORY ) + { + beh->listFactory = func.id; + + // Store the list pattern for this function + r = scriptFunctions[func.id]->RegisterListPattern(decl, listPattern); + + if( listPattern ) + listPattern->Destroy(this); + + if( r < 0 ) + return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + else + { + // Is this the copy factory? + asCDataType paramType = func.parameterTypes[func.parameterTypes.GetLength()-1]; + + // If the parameter is object, and const reference for input, + // and same type as this class, then this is a copy constructor. + if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && func.inOutFlags[func.parameterTypes.GetLength()-1] == asTM_INREF && paramType.GetTypeInfo() == objectType ) + beh->copyfactory = func.id; + } + } + } + else if( behaviour == asBEHAVE_ADDREF ) + { + // Must be a ref type and must not have asOBJ_NOHANDLE, nor asOBJ_SCOPED + if( !(func.objectType->flags & asOBJ_REF) || + (func.objectType->flags & asOBJ_NOHANDLE) || + (func.objectType->flags & asOBJ_SCOPED) || + (func.objectType->flags & asOBJ_NOCOUNT) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( beh->addref ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + func.id = beh->addref = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_RELEASE ) + { + // Must be a ref type and must not have asOBJ_NOHANDLE + if( !(func.objectType->flags & asOBJ_REF) || + (func.objectType->flags & asOBJ_NOHANDLE) || + (func.objectType->flags & asOBJ_NOCOUNT) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( beh->release ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + func.id = beh->release = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_TEMPLATE_CALLBACK ) + { + // Must be a template type + if( !(func.objectType->flags & asOBJ_TEMPLATE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( beh->templateCallback ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that the return type is bool + if( func.returnType != asCDataType::CreatePrimitive(ttBool, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that there are two parameters + if( func.parameterTypes.GetLength() != 2 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // The first parameter must be an inref (to receive the object type), and + // the second must be a bool out ref (to return if the type should or shouldn't be garbage collected) + if( func.inOutFlags[0] != asTM_INREF || func.inOutFlags[1] != asTM_OUTREF || !func.parameterTypes[1].IsEqualExceptRef(asCDataType::CreatePrimitive(ttBool, false)) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + func.id = beh->templateCallback = AddBehaviourFunction(func, internal); + } + else if( behaviour >= asBEHAVE_FIRST_GC && + behaviour <= asBEHAVE_LAST_GC ) + { + // Only allow GC behaviours for types registered to be garbage collected + if( !(func.objectType->flags & asOBJ_GC) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Verify parameter count + if( (behaviour == asBEHAVE_GETREFCOUNT || + behaviour == asBEHAVE_SETGCFLAG || + behaviour == asBEHAVE_GETGCFLAG) && + func.parameterTypes.GetLength() != 0 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( (behaviour == asBEHAVE_ENUMREFS || + behaviour == asBEHAVE_RELEASEREFS) && + func.parameterTypes.GetLength() != 1 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify return type + if( behaviour == asBEHAVE_GETREFCOUNT && + func.returnType != asCDataType::CreatePrimitive(ttInt, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( behaviour == asBEHAVE_GETGCFLAG && + func.returnType != asCDataType::CreatePrimitive(ttBool, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( (behaviour == asBEHAVE_SETGCFLAG || + behaviour == asBEHAVE_ENUMREFS || + behaviour == asBEHAVE_RELEASEREFS) && + func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( behaviour == asBEHAVE_GETREFCOUNT ) + func.id = beh->gcGetRefCount = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_SETGCFLAG ) + func.id = beh->gcSetFlag = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_GETGCFLAG ) + func.id = beh->gcGetFlag = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_ENUMREFS ) + func.id = beh->gcEnumReferences = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_RELEASEREFS ) + func.id = beh->gcReleaseAllReferences = AddBehaviourFunction(func, internal); + } +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0. 2014-10-24 + else if( behaviour == asBEHAVE_IMPLICIT_VALUE_CAST || + behaviour == asBEHAVE_VALUE_CAST ) + { + // There are two allowed signatures + // 1. type f() + // 2. void f(?&out) + + if( !(func.parameterTypes.GetLength() == 1 && func.parameterTypes[0].GetTokenType() == ttQuestion && func.inOutFlags[0] == asTM_OUTREF && func.returnType.GetTokenType() == ttVoid) && + !(func.parameterTypes.GetLength() == 0 && func.returnType.GetTokenType() != ttVoid) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // It is not allowed to implement a value cast to bool + if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, false)) ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + asCString decl; + decl += func.returnType.Format(defaultNamespace); + decl += behaviour == asBEHAVE_VALUE_CAST ? " opConv(" : " opImplConv("; + if( func.parameterTypes.GetLength() ) + decl += "?&out"; + decl += ")"; + func.id = RegisterMethodToObjectType(objectType, decl.AddressOf(), funcPointer, callConv, auxiliary); + } + // Deprecated since 2.30.0, 2014-12-30 + else if( behaviour == asBEHAVE_REF_CAST || + behaviour == asBEHAVE_IMPLICIT_REF_CAST ) + { + // There are two allowed signatures + // 1. obj @f() + // 2. void f(?&out) + + if( !(func.parameterTypes.GetLength() == 0 && func.returnType.IsObjectHandle()) && + !(func.parameterTypes.GetLength() == 1 && func.parameterTypes[0].GetTokenType() == ttQuestion && func.inOutFlags[0] == asTM_OUTREF && func.returnType.GetTokenType() == ttVoid) ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Currently it is not supported to register const overloads for the ref cast behaviour + if( func.IsReadOnly() ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + asCString decl; + decl += func.returnType.Format(defaultNamespace); + if( internal.returnAutoHandle ) + decl += "+"; + decl += behaviour == asBEHAVE_REF_CAST ? " opCast(" : " opImplCast("; + if( func.parameterTypes.GetLength() ) + decl += "?&out"; + decl += ")"; + func.id = RegisterMethodToObjectType(objectType, decl.AddressOf(), funcPointer, callConv, auxiliary); + } +#endif + else if ( behaviour == asBEHAVE_GET_WEAKREF_FLAG ) + { + // This behaviour is only allowed for reference types + if( !(func.objectType->flags & asOBJ_REF) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Don't allow it if the type is registered with nohandle or scoped + if( func.objectType->flags & (asOBJ_NOHANDLE|asOBJ_SCOPED) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + // Verify that the return type is a reference since it needs to return a pointer to an asISharedBool + if( !func.returnType.IsReference() ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() != 0 ) + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + if( beh->getWeakRefFlag ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + func.id = beh->getWeakRefFlag = AddBehaviourFunction(func, internal); + } + else + { + asASSERT(false); + + return ConfigError(asINVALID_ARG, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + + if( func.id < 0 ) + return ConfigError(func.id, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // Return function id as success + return func.id; +} + +int asCScriptEngine::SetTemplateRestrictions(asCObjectType *templateType, asCScriptFunction *func, const char *caller, const char *decl) +{ + asASSERT(templateType->flags && asOBJ_TEMPLATE); + + for (asUINT subTypeIdx = 0; subTypeIdx < templateType->templateSubTypes.GetLength(); subTypeIdx++) + { + if (func->returnType.GetTypeInfo() == templateType->templateSubTypes[subTypeIdx].GetTypeInfo()) + { + if (func->returnType.IsObjectHandle()) + templateType->acceptValueSubType = false; + else if (!func->returnType.IsReference()) + templateType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if (!func->returnType.IsObjectHandle() && !func->returnType.IsReference()) + return ConfigError(asNOT_SUPPORTED, caller, templateType->name.AddressOf(), decl); + } + + for (asUINT n = 0; n < func->parameterTypes.GetLength(); n++) + { + if (func->parameterTypes[n].GetTypeInfo() == templateType->templateSubTypes[subTypeIdx].GetTypeInfo()) + { + // TODO: If unsafe references are allowed, then inout references allow value types + if (func->parameterTypes[n].IsObjectHandle() || (func->parameterTypes[n].IsReference() && func->inOutFlags[n] == asTM_INOUTREF)) + templateType->acceptValueSubType = false; + else if (!func->parameterTypes[n].IsReference()) + templateType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if (!func->parameterTypes[n].IsObjectHandle() && !func->parameterTypes[n].IsReference()) + return ConfigError(asNOT_SUPPORTED, caller, templateType->name.AddressOf(), decl); + } + } + } + + return asSUCCESS; +} + +int asCScriptEngine::VerifyVarTypeNotInFunction(asCScriptFunction *func) +{ + // Don't allow var type in this function + if( func->returnType.GetTokenType() == ttQuestion ) + return asINVALID_DECLARATION; + + for( unsigned int n = 0; n < func->parameterTypes.GetLength(); n++ ) + if( func->parameterTypes[n].GetTokenType() == ttQuestion ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCScriptEngine::AddBehaviourFunction(asCScriptFunction &func, asSSystemFunctionInterface &internal) +{ + asUINT n; + + int id = GetNextScriptFunctionId(); + + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + if( newInterface == 0 ) + return asOUT_OF_MEMORY; + + asCScriptFunction *f = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + if( f == 0 ) + { + asDELETE(newInterface, asSSystemFunctionInterface); + return asOUT_OF_MEMORY; + } + + asASSERT(func.name != "" && func.name != "f"); + f->name = func.name; + f->sysFuncIntf = newInterface; + f->returnType = func.returnType; + f->objectType = func.objectType; + if( f->objectType ) + f->objectType->AddRefInternal(); + f->id = id; + f->isReadOnly = func.isReadOnly; + f->accessMask = defaultAccessMask; + f->parameterTypes = func.parameterTypes; + f->parameterNames = func.parameterNames; + f->inOutFlags = func.inOutFlags; + for( n = 0; n < func.defaultArgs.GetLength(); n++ ) + if( func.defaultArgs[n] ) + f->defaultArgs.PushLast(asNEW(asCString)(*func.defaultArgs[n])); + else + f->defaultArgs.PushLast(0); + + AddScriptFunction(f); + + // If parameter type from other groups are used, add references + currentGroup->AddReferencesForFunc(this, f); + + return id; +} + +// interface +int asCScriptEngine::RegisterGlobalProperty(const char *declaration, void *pointer) +{ + // Don't accept a null pointer + if( pointer == 0 ) + return ConfigError(asINVALID_ARG, "RegisterGlobalProperty", declaration, 0); + + asCDataType type; + asCString name; + + int r; + asCBuilder bld(this, 0); + if( (r = bld.VerifyProperty(0, declaration, name, type, defaultNamespace)) < 0 ) + return ConfigError(r, "RegisterGlobalProperty", declaration, 0); + + // Don't allow registering references as global properties + if( type.IsReference() ) + return ConfigError(asINVALID_TYPE, "RegisterGlobalProperty", declaration, 0); + + // Store the property info + asCGlobalProperty *prop = AllocateGlobalProperty(); + prop->name = name; + prop->nameSpace = defaultNamespace; + prop->type = type; + prop->accessMask = defaultAccessMask; + + prop->SetRegisteredAddress(pointer); + varAddressMap.Insert(prop->GetAddressOfValue(), prop); + + registeredGlobalProps.Put(prop); + prop->AddRef(); + currentGroup->globalProps.PushLast(prop); + + currentGroup->AddReferencesForType(this, type.GetTypeInfo()); + + return asSUCCESS; +} + +// internal +asCGlobalProperty *asCScriptEngine::AllocateGlobalProperty() +{ + asCGlobalProperty *prop = asNEW(asCGlobalProperty); + if( prop == 0 ) + { + // Out of memory + return 0; + } + + // First check the availability of a free slot + if( freeGlobalPropertyIds.GetLength() ) + { + prop->id = freeGlobalPropertyIds.PopLast(); + globalProperties[prop->id] = prop; + return prop; + } + + prop->id = (asUINT)globalProperties.GetLength(); + globalProperties.PushLast(prop); + return prop; +} + +// internal +void asCScriptEngine::RemoveGlobalProperty(asCGlobalProperty *prop) +{ + int index = globalProperties.IndexOf(prop); + if( index >= 0 ) + { + freeGlobalPropertyIds.PushLast(index); + globalProperties[index] = 0; + + asSMapNode *node; + varAddressMap.MoveTo(&node, prop->GetAddressOfValue()); + asASSERT(node); + if( node ) + varAddressMap.Erase(node); + + prop->Release(); + } +} + +// interface +asUINT asCScriptEngine::GetGlobalPropertyCount() const +{ + return asUINT(registeredGlobalProps.GetSize()); +} + +// interface +// TODO: If the typeId ever encodes the const flag, then the isConst parameter should be removed +int asCScriptEngine::GetGlobalPropertyByIndex(asUINT index, const char **name, const char **nameSpace, int *typeId, bool *isConst, const char **configGroup, void **pointer, asDWORD *accessMask) const +{ + const asCGlobalProperty *prop = registeredGlobalProps.Get(index); + if( !prop ) + return asINVALID_ARG; + + if( name ) *name = prop->name.AddressOf(); + if( nameSpace ) *nameSpace = prop->nameSpace->name.AddressOf(); + if( typeId ) *typeId = GetTypeIdFromDataType(prop->type); + if( isConst ) *isConst = prop->type.IsReadOnly(); + if( pointer ) *pointer = prop->GetRegisteredAddress(); + if( accessMask ) *accessMask = prop->accessMask; + + if( configGroup ) + { + asCConfigGroup *group = FindConfigGroupForGlobalVar(index); + if( group ) + *configGroup = group->groupName.AddressOf(); + else + *configGroup = 0; + } + + return asSUCCESS; +} + +// interface +int asCScriptEngine::GetGlobalPropertyIndexByName(const char *name) const +{ + asSNameSpace *ns = defaultNamespace; + + // Find the global var id + while( ns ) + { + int id = registeredGlobalProps.GetFirstIndex(ns, name); + if( id >= 0 ) + return id; + + // Recursively search parent namespace + ns = GetParentNameSpace(ns); + } + + return asNO_GLOBAL_VAR; +} + +// interface +int asCScriptEngine::GetGlobalPropertyIndexByDecl(const char *decl) const +{ + // This const cast is OK. The builder won't modify the engine + asCBuilder bld(const_cast(this), 0); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCString name; + asSNameSpace *ns; + asCDataType dt; + int r = bld.ParseVariableDeclaration(decl, defaultNamespace, name, ns, dt); + if( r < 0 ) + return r; + + // Search for a match + while( ns ) + { + int id = registeredGlobalProps.GetFirstIndex(ns, name, asCCompGlobPropType(dt)); + if( id >= 0 ) + return id; + + ns = GetParentNameSpace(ns); + } + + return asNO_GLOBAL_VAR; +} + +// interface +int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ + if( obj == 0 ) + return ConfigError(asINVALID_ARG, "RegisterObjectMethod", obj, declaration); + + // Determine the object type + asCDataType dt; + asCBuilder bld(this, 0); + int r = bld.ParseDataType(obj, &dt, defaultNamespace); + if( r < 0 ) + return ConfigError(r, "RegisterObjectMethod", obj, declaration); + + // Don't allow application to modify primitives or handles + if( dt.GetTypeInfo() == 0 || (dt.IsObjectHandle() && !(dt.GetTypeInfo()->GetFlags() & asOBJ_IMPLICIT_HANDLE))) + return ConfigError(asINVALID_ARG, "RegisterObjectMethod", obj, declaration); + + // Don't allow application to modify built-in types or funcdefs + if( dt.GetTypeInfo() == &functionBehaviours || + dt.GetTypeInfo() == &scriptTypeBehaviours || + CastToFuncdefType(dt.GetTypeInfo()) ) + return ConfigError(asINVALID_ARG, "RegisterObjectMethod", obj, declaration); + + // Don't allow modifying generated template instances + if( dt.GetTypeInfo() && (dt.GetTypeInfo()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(CastToObjectType(dt.GetTypeInfo())) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectMethod", obj, declaration); + + return RegisterMethodToObjectType(CastToObjectType(dt.GetTypeInfo()), declaration, funcPointer, callConv, auxiliary); +} + +// internal +int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); +#endif + + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(true, funcPointer, callConv, auxiliary, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + + // TODO: cleanup: This is identical to what is in RegisterMethodToObjectType + // If the object type is a template, make sure there are no generated instances already + if( objectType->flags & asOBJ_TEMPLATE ) + { + for( asUINT n = 0; n < generatedTemplateTypes.GetLength(); n++ ) + { + asCObjectType *tmpl = generatedTemplateTypes[n]; + if( tmpl->name == objectType->name && + tmpl->nameSpace == objectType->nameSpace && + !(tmpl->templateSubTypes[0].GetTypeInfo() && (tmpl->templateSubTypes[0].GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE)) ) + { + asCString msg; + msg.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, asCDataType::CreateType(tmpl, false).Format(tmpl->nameSpace).AddressOf()); + WriteMessage("",0,0, asMSGTYPE_ERROR, msg.AddressOf()); + return ConfigError(asERROR, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + } + } + + isPrepared = false; + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + if( newInterface == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + if( func == 0 ) + { + asDELETE(newInterface, asSSystemFunctionInterface); + return ConfigError(asOUT_OF_MEMORY, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + + func->sysFuncIntf = newInterface; + func->objectType = objectType; + func->objectType->AddRefInternal(); + + asCBuilder bld(this, 0); + r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + + // Check name conflicts + r = bld.CheckNameConflictMember(objectType, func->name.AddressOf(), 0, 0, false); + if( r < 0 ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + + // Check against duplicate methods + if( func->name == "opConv" || func->name == "opImplConv" || func->name == "opCast" || func->name == "opImplCast" ) + { + // opConv and opCast are special methods that the compiler differentiates between by the return type + for( asUINT n = 0; n < func->objectType->methods.GetLength(); n++ ) + { + asCScriptFunction *f = scriptFunctions[func->objectType->methods[n]]; + if( f->name == func->name && + f->IsSignatureExceptNameEqual(func) ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + } + } + else + { + for( asUINT n = 0; n < func->objectType->methods.GetLength(); n++ ) + { + asCScriptFunction *f = scriptFunctions[func->objectType->methods[n]]; + if( f->name == func->name && + f->IsSignatureExceptNameAndReturnTypeEqual(func) ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + } + } + + func->id = GetNextScriptFunctionId(); + func->objectType->methods.PushLast(func->id); + func->accessMask = defaultAccessMask; + AddScriptFunction(func); + + // If parameter type from other groups are used, add references + currentGroup->AddReferencesForFunc(this, func); + + // Check if the method restricts that use of the template to value types or reference types + if( func->objectType->flags & asOBJ_TEMPLATE ) + { + r = SetTemplateRestrictions(func->objectType, func, "RegisterObjectMethod", declaration); + if (r < 0) + return r; + } + + // TODO: beh.copy member will be removed, so this is not necessary + // Is this the default copy behaviour? + if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 && func->isReadOnly == false && + ((objectType->flags & asOBJ_SCRIPT_OBJECT) || func->parameterTypes[0].IsEqualExceptRefAndConst(asCDataType::CreateType(func->objectType, false))) ) + { + if( func->objectType->beh.copy != 0 ) + return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + + func->objectType->beh.copy = func->id; + func->AddRefInternal(); + } + + // Return the function id as success + return func->id; +} + +// interface +int asCScriptEngine::RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterGlobalFunction", declaration, 0); +#endif + + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(false, funcPointer, callConv, auxiliary, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterGlobalFunction", declaration, 0); + + isPrepared = false; + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + if( newInterface == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterGlobalFunction", declaration, 0); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + if( func == 0 ) + { + asDELETE(newInterface, asSSystemFunctionInterface); + return ConfigError(asOUT_OF_MEMORY, "RegisterGlobalFunction", declaration, 0); + } + + func->sysFuncIntf = newInterface; + + asCBuilder bld(this, 0); + r = bld.ParseFunctionDeclaration(0, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle, defaultNamespace); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION, "RegisterGlobalFunction", declaration, 0); + } + + // TODO: namespace: What if the declaration defined an explicit namespace? + func->nameSpace = defaultNamespace; + + // Check name conflicts + r = bld.CheckNameConflict(func->name.AddressOf(), 0, 0, defaultNamespace); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN, "RegisterGlobalFunction", declaration, 0); + } + + // Make sure the function is not identical to a previously registered function + asUINT n; + const asCArray &idxs = registeredGlobalFuncs.GetIndexes(func->nameSpace, func->name); + for( n = 0; n < idxs.GetLength(); n++ ) + { + asCScriptFunction *f = registeredGlobalFuncs.Get(idxs[n]); + if( f->IsSignatureExceptNameAndReturnTypeEqual(func) ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asALREADY_REGISTERED, "RegisterGlobalFunction", declaration, 0); + } + } + + func->id = GetNextScriptFunctionId(); + AddScriptFunction(func); + + currentGroup->scriptFunctions.PushLast(func); + func->accessMask = defaultAccessMask; + registeredGlobalFuncs.Put(func); + + // If parameter type from other groups are used, add references + currentGroup->AddReferencesForFunc(this, func); + + // Return the function id as success + return func->id; +} + +// interface +asUINT asCScriptEngine::GetGlobalFunctionCount() const +{ + // Don't count the builtin delegate factory + return asUINT(registeredGlobalFuncs.GetSize()-1); +} + +// interface +asIScriptFunction *asCScriptEngine::GetGlobalFunctionByIndex(asUINT index) const +{ + // Don't count the builtin delegate factory + index++; + + if( index >= registeredGlobalFuncs.GetSize() ) + return 0; + + return static_cast(const_cast(registeredGlobalFuncs.Get(index))); +} + +// interface +asIScriptFunction *asCScriptEngine::GetGlobalFunctionByDecl(const char *decl) const +{ + asCBuilder bld(const_cast(this), 0); + + // Don't write parser errors to the message callback + bld.silent = true; + + asCScriptFunction func(const_cast(this), 0, asFUNC_DUMMY); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false, 0, 0, defaultNamespace); + if( r < 0 ) + return 0; + + asSNameSpace *ns = defaultNamespace; + // Search script functions for matching interface + while( ns ) + { + asIScriptFunction *f = 0; + const asCArray &idxs = registeredGlobalFuncs.GetIndexes(ns, func.name); + for( unsigned int n = 0; n < idxs.GetLength(); n++ ) + { + const asCScriptFunction *funcPtr = registeredGlobalFuncs.Get(idxs[n]); + if( funcPtr->objectType == 0 && + func.returnType == funcPtr->returnType && + func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() + ) + { + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) + { + if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( f == 0 ) + f = const_cast(funcPtr); + else + // Multiple functions + return 0; + } + } + } + + if( f ) + return f; + + // Recursively search parent namespaces + ns = GetParentNameSpace(ns); + } + + return 0; +} + + +asCTypeInfo *asCScriptEngine::GetRegisteredType(const asCString &type, asSNameSpace *ns) const +{ + asSMapNode *cursor; + if( allRegisteredTypes.MoveTo(&cursor, asSNameSpaceNamePair(ns, type)) ) + return cursor->value; + + return 0; +} + + + + +void asCScriptEngine::PrepareEngine() +{ + if( isPrepared ) return; + if( configFailed ) return; + + asUINT n; + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + // Determine the host application interface + if( scriptFunctions[n] && scriptFunctions[n]->funcType == asFUNC_SYSTEM ) + { + if( scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_FUNC || + scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_METHOD ) + PrepareSystemFunctionGeneric(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this); + else + PrepareSystemFunction(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this); + } + } + + // Validate object type registrations + for( n = 0; n < registeredObjTypes.GetLength(); n++ ) + { + asCObjectType *type = registeredObjTypes[n]; + if( type && !(type->flags & asOBJ_SCRIPT_OBJECT) ) + { + bool missingBehaviour = false; + const char *infoMsg = 0; + + // Verify that GC types have all behaviours + if( type->flags & asOBJ_GC ) + { + if( type->beh.addref == 0 || + type->beh.release == 0 || + type->beh.gcGetRefCount == 0 || + type->beh.gcSetFlag == 0 || + type->beh.gcGetFlag == 0 || + type->beh.gcEnumReferences == 0 || + type->beh.gcReleaseAllReferences == 0 ) + { + infoMsg = TXT_GC_REQUIRE_ADD_REL_GC_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that scoped ref types have the release behaviour + else if( type->flags & asOBJ_SCOPED ) + { + if( type->beh.release == 0 ) + { + infoMsg = TXT_SCOPE_REQUIRE_REL_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that ref types have add ref and release behaviours + else if( (type->flags & asOBJ_REF) && + !(type->flags & asOBJ_NOHANDLE) && + !(type->flags & asOBJ_NOCOUNT) ) + { + if( type->beh.addref == 0 || + type->beh.release == 0 ) + { + infoMsg = TXT_REF_REQUIRE_ADD_REL_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that non-pod value types have the constructor and destructor registered + else if( (type->flags & asOBJ_VALUE) && + !(type->flags & asOBJ_POD) ) + { + if( type->beh.constructors.GetLength() == 0 || + type->beh.destruct == 0 ) + { + infoMsg = TXT_NON_POD_REQUIRE_CONSTR_DESTR_BEHAVIOUR; + missingBehaviour = true; + } + } + + if( missingBehaviour ) + { + asCString str; + str.Format(TXT_TYPE_s_IS_MISSING_BEHAVIOURS, type->name.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, infoMsg); + ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); + } + } + } + + isPrepared = true; +} + +int asCScriptEngine::ConfigError(int err, const char *funcName, const char *arg1, const char *arg2) +{ + configFailed = true; + if( funcName ) + { + asCString str; + if( arg1 ) + { + if( arg2 ) + str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_AND_s_d, funcName, arg1, arg2, err); + else + str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, funcName, arg1, err); + } + else + str.Format(TXT_FAILED_IN_FUNC_s_d, funcName, err); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + } + return err; +} + +// interface +int asCScriptEngine::RegisterDefaultArrayType(const char *type) +{ + asCBuilder bld(this, 0); + asCDataType dt; + int r = bld.ParseDataType(type, &dt, defaultNamespace); + if( r < 0 ) return r; + + if( dt.GetTypeInfo() == 0 || + !(dt.GetTypeInfo()->GetFlags() & asOBJ_TEMPLATE) ) + return asINVALID_TYPE; + + defaultArrayObjectType = CastToObjectType(dt.GetTypeInfo()); + defaultArrayObjectType->AddRefInternal(); + + return 0; +} + +// interface +int asCScriptEngine::GetDefaultArrayTypeId() const +{ + if( defaultArrayObjectType ) + return GetTypeIdFromDataType(asCDataType::CreateType(defaultArrayObjectType, false)); + + return asINVALID_TYPE; +} + +// interface +int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary) +{ + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(false, funcPointer, callConv, auxiliary, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterStringFactory", datatype, 0); + +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterStringFactory", datatype, 0); +#else + if( callConv != asCALL_CDECL && + callConv != asCALL_STDCALL && + callConv != asCALL_THISCALL_ASGLOBAL && + callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterStringFactory", datatype, 0); +#endif + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + if( newInterface == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterStringFactory", datatype, 0); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + if( func == 0 ) + { + asDELETE(newInterface, asSSystemFunctionInterface); + return ConfigError(asOUT_OF_MEMORY, "RegisterStringFactory", datatype, 0); + } + + func->name = "$str"; + func->sysFuncIntf = newInterface; + + asCBuilder bld(this, 0); + + asCDataType dt; + r = bld.ParseDataType(datatype, &dt, defaultNamespace, true); + if( r < 0 ) + { + // Set as dummy before deleting + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_TYPE, "RegisterStringFactory", datatype, 0); + } + + func->returnType = dt; + func->parameterTypes.PushLast(asCDataType::CreatePrimitive(ttInt, true)); + func->inOutFlags.PushLast(asTM_NONE); + asCDataType parm1 = asCDataType::CreatePrimitive(ttUInt8, true); + parm1.MakeReference(true); + func->parameterTypes.PushLast(parm1); + func->inOutFlags.PushLast(asTM_INREF); + func->id = GetNextScriptFunctionId(); + AddScriptFunction(func); + + stringFactory = func; + + if( func->returnType.GetTypeInfo() ) + { + asCConfigGroup *group = FindConfigGroupForTypeInfo(func->returnType.GetTypeInfo()); + if( group == 0 ) group = &defaultGroup; + group->scriptFunctions.PushLast(func); + } + + // Register function id as success + return func->id; +} + +// interface +int asCScriptEngine::GetStringFactoryReturnTypeId(asDWORD *flags) const +{ + if( stringFactory == 0 ) + return asNO_FUNCTION; + + return stringFactory->GetReturnTypeId(flags); +} + +// internal +asCModule *asCScriptEngine::GetModule(const char *name, bool create) +{ + // Accept null as well as zero-length string + if( name == 0 ) name = ""; + + asCModule *retModule = 0; + + ACQUIRESHARED(engineRWLock); + if( lastModule && lastModule->name == name ) + retModule = lastModule; + else + { + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < scriptModules.GetLength(); ++n ) + if( scriptModules[n] && scriptModules[n]->name == name ) + { + retModule = scriptModules[n]; + break; + } + } + RELEASESHARED(engineRWLock); + + if( retModule ) + { + ACQUIREEXCLUSIVE(engineRWLock); + lastModule = retModule; + RELEASEEXCLUSIVE(engineRWLock); + + return retModule; + } + + if( create ) + { + retModule = asNEW(asCModule)(name, this); + if( retModule == 0 ) + { + // Out of memory + return 0; + } + + ACQUIREEXCLUSIVE(engineRWLock); + scriptModules.PushLast(retModule); + lastModule = retModule; + RELEASEEXCLUSIVE(engineRWLock); + } + + return retModule; +} + +asCModule *asCScriptEngine::GetModuleFromFuncId(int id) +{ + if( id < 0 ) return 0; + if( id >= (int)scriptFunctions.GetLength() ) return 0; + asCScriptFunction *func = scriptFunctions[id]; + if( func == 0 ) return 0; + return func->module; +} + +// internal +int asCScriptEngine::RequestBuild() +{ + ACQUIREEXCLUSIVE(engineRWLock); + if( isBuilding ) + { + RELEASEEXCLUSIVE(engineRWLock); + return asBUILD_IN_PROGRESS; + } + isBuilding = true; + RELEASEEXCLUSIVE(engineRWLock); + + return 0; +} + +// internal +void asCScriptEngine::BuildCompleted() +{ + // Always free up pooled memory after a completed build + memoryMgr.FreeUnusedMemory(); + + isBuilding = false; +} + +void asCScriptEngine::RemoveTemplateInstanceType(asCObjectType *t) +{ + // If there is a module that still owns the generated type, then don't remove it + if( t->module ) + return; + + // Don't remove it if there are external refernces + if( t->externalRefCount.get() ) + return; + + // Only remove the template instance type if no config group is using it + if( defaultGroup.generatedTemplateInstances.Exists(t) ) + return; + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + if( configGroups[n]->generatedTemplateInstances.Exists(t) ) + return; + + t->DestroyInternal(); + templateInstanceTypes.RemoveValue(t); + generatedTemplateTypes.RemoveValue(t); + t->ReleaseInternal(); +} + +// internal +asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes, asCModule *requestingModule) +{ + asUINT n; + + // Is there any template instance type or template specialization already with this subtype? + for( n = 0; n < templateInstanceTypes.GetLength(); n++ ) + { + asCObjectType *type = templateInstanceTypes[n]; + if( type && + type->name == templateType->name && + type->templateSubTypes == subTypes ) + { + // If the template instance is generated, then the module should hold a reference + // to it so the config group can determine see that the template type is in use. + // Template specializations will be treated as normal types + if( requestingModule && generatedTemplateTypes.Exists(type) ) + { + if( type->module == 0 ) + { + // Set the ownership of this template type + // It may be without ownership if it was previously created from application with for example GetTypeInfoByDecl + type->module = requestingModule; + } + if( !requestingModule->templateInstances.Exists(type) ) + { + requestingModule->templateInstances.PushLast(type); + type->AddRefInternal(); + } + } + + return templateInstanceTypes[n]; + } + } + + // No previous template instance exists + + // Make sure this template supports the subtype + for( n = 0; n < subTypes.GetLength(); n++ ) + { + if( !templateType->acceptValueSubType && (subTypes[n].IsPrimitive() || (subTypes[n].GetTypeInfo()->flags & asOBJ_VALUE)) ) + return 0; + + if( !templateType->acceptRefSubType && (subTypes[n].IsObject() && (subTypes[n].GetTypeInfo()->flags & asOBJ_REF)) ) + return 0; + } + + // Create a new template instance type based on the templateType + asCObjectType *ot = asNEW(asCObjectType)(this); + if( ot == 0 ) + { + // Out of memory + return 0; + } + + ot->templateSubTypes = subTypes; + ot->flags = templateType->flags; + ot->size = templateType->size; + ot->name = templateType->name; + ot->nameSpace = templateType->nameSpace; + + // If the template is being requested from a module, then the module should hold a reference to the type + if( requestingModule ) + { + // Set the ownership of this template type + ot->module = requestingModule; + requestingModule->templateInstances.PushLast(ot); + ot->AddRefInternal(); + } + else + { + // If the template type is not requested directly from a module, then set the ownership + // of it to the same module as one of the subtypes. If none of the subtypes are owned by] + // any module, the template instance will be without ownership and can be removed from the + // engine at any time (unless the application holds an external reference). + for( n = 0; n < subTypes.GetLength(); n++ ) + { + if( subTypes[n].GetTypeInfo() ) + { + ot->module = subTypes[n].GetTypeInfo()->module; + if( ot->module ) + { + ot->module->templateInstances.PushLast(ot); + ot->AddRefInternal(); + break; + } + } + } + } + + // Before filling in the methods, call the template instance callback behaviour to validate the type + if( templateType->beh.templateCallback ) + { + // If the validation is deferred then the validation will be done later, + // so it is necessary to continue the preparation of the template instance type + if( !deferValidationOfTemplateTypes ) + { + asCScriptFunction *callback = scriptFunctions[templateType->beh.templateCallback]; + + bool dontGarbageCollect = false; + if( !CallGlobalFunctionRetBool(ot, &dontGarbageCollect, callback->sysFuncIntf, callback) ) + { + // The type cannot be instantiated + ot->templateSubTypes.SetLength(0); + if( ot->module ) + { + ot->module->templateInstances.RemoveValue(ot); + ot->ReleaseInternal(); + } + ot->ReleaseInternal(); + return 0; + } + + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + ot->flags &= ~asOBJ_GC; + } + + ot->beh.templateCallback = templateType->beh.templateCallback; + scriptFunctions[ot->beh.templateCallback]->AddRefInternal(); + } + + ot->methods = templateType->methods; + for( n = 0; n < ot->methods.GetLength(); n++ ) + scriptFunctions[ot->methods[n]]->AddRefInternal(); + + if( templateType->flags & asOBJ_REF ) + { + // Store the real factory in the constructor. This is used by the CreateScriptObject function. + // Otherwise it wouldn't be necessary to store the real factory ids. + ot->beh.construct = templateType->beh.factory; + ot->beh.constructors = templateType->beh.factories; + } + else + { + ot->beh.construct = templateType->beh.construct; + ot->beh.constructors = templateType->beh.constructors; + } + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + scriptFunctions[ot->beh.constructors[n]]->AddRefInternal(); + + + // Before proceeding with the generation of the template functions for the template instance it is necessary + // to include the new template instance type in the list of known types, otherwise it is possible that we get + // a infinite recursive loop as the template instance type is requested again during the generation of the + // template functions. + templateInstanceTypes.PushLast(ot); + + // Store the template instance types that have been created automatically by the engine from a template type + // The object types in templateInstanceTypes that are not also in generatedTemplateTypes are registered template specializations + generatedTemplateTypes.PushLast(ot); + + + // As the new template type is instantiated the engine should + // generate new functions to substitute the ones with the template subtype. + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + { + int funcId = ot->beh.constructors[n]; + asCScriptFunction *func = scriptFunctions[funcId]; + + if( GenerateNewTemplateFunction(templateType, ot, func, &func) ) + { + // Release the old function, the new one already has its ref count set to 1 + scriptFunctions[funcId]->ReleaseInternal(); + ot->beh.constructors[n] = func->id; + + if( ot->beh.construct == funcId ) + ot->beh.construct = func->id; + } + } + + ot->beh.factory = 0; + + if( templateType->flags & asOBJ_REF ) + { + // Generate factory stubs for each of the factories + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + { + asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, ot->beh.constructors[n]); + + ot->beh.factories.PushLast(func->id); + + // Set the default factory as well + if( ot->beh.constructors[n] == ot->beh.construct ) + ot->beh.factory = func->id; + } + } + else + { + // Generate factory stubs for each of the constructors + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + { + asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, ot->beh.constructors[n]); + + if( ot->beh.constructors[n] == ot->beh.construct ) + ot->beh.construct = func->id; + + // Release previous constructor + scriptFunctions[ot->beh.constructors[n]]->ReleaseInternal(); + + ot->beh.constructors[n] = func->id; + } + } + + // Generate stub for the list factory as well + if( templateType->beh.listFactory ) + { + asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, templateType->beh.listFactory); + + // Rename the function to easily identify it in LoadByteCode + func->name = "$list"; + + ot->beh.listFactory = func->id; + } + + ot->beh.addref = templateType->beh.addref; + if( scriptFunctions[ot->beh.addref] ) scriptFunctions[ot->beh.addref]->AddRefInternal(); + ot->beh.release = templateType->beh.release; + if( scriptFunctions[ot->beh.release] ) scriptFunctions[ot->beh.release]->AddRefInternal(); + ot->beh.destruct = templateType->beh.destruct; + if( scriptFunctions[ot->beh.destruct] ) scriptFunctions[ot->beh.destruct]->AddRefInternal(); + ot->beh.copy = templateType->beh.copy; + if( scriptFunctions[ot->beh.copy] ) scriptFunctions[ot->beh.copy]->AddRefInternal(); + ot->beh.gcGetRefCount = templateType->beh.gcGetRefCount; + if( scriptFunctions[ot->beh.gcGetRefCount] ) scriptFunctions[ot->beh.gcGetRefCount]->AddRefInternal(); + ot->beh.gcSetFlag = templateType->beh.gcSetFlag; + if( scriptFunctions[ot->beh.gcSetFlag] ) scriptFunctions[ot->beh.gcSetFlag]->AddRefInternal(); + ot->beh.gcGetFlag = templateType->beh.gcGetFlag; + if( scriptFunctions[ot->beh.gcGetFlag] ) scriptFunctions[ot->beh.gcGetFlag]->AddRefInternal(); + ot->beh.gcEnumReferences = templateType->beh.gcEnumReferences; + if( scriptFunctions[ot->beh.gcEnumReferences] ) scriptFunctions[ot->beh.gcEnumReferences]->AddRefInternal(); + ot->beh.gcReleaseAllReferences = templateType->beh.gcReleaseAllReferences; + if( scriptFunctions[ot->beh.gcReleaseAllReferences] ) scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRefInternal(); + ot->beh.getWeakRefFlag = templateType->beh.getWeakRefFlag; + if( scriptFunctions[ot->beh.getWeakRefFlag] ) scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal(); + + // As the new template type is instantiated, the engine should + // generate new functions to substitute the ones with the template subtype. + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + int funcId = ot->methods[n]; + asCScriptFunction *func = scriptFunctions[funcId]; + + if( GenerateNewTemplateFunction(templateType, ot, func, &func) ) + { + // Release the old function, the new one already has its ref count set to 1 + scriptFunctions[funcId]->ReleaseInternal(); + ot->methods[n] = func->id; + } + } + + // Increase ref counter for sub type if it is an object type + for( n = 0; n < ot->templateSubTypes.GetLength(); n++ ) + if( ot->templateSubTypes[n].GetTypeInfo() ) + ot->templateSubTypes[n].GetTypeInfo()->AddRefInternal(); + + // Copy the properties to the template instance + for( n = 0; n < templateType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = templateType->properties[n]; + ot->properties.PushLast(asNEW(asCObjectProperty)(*prop)); + if( prop->type.GetTypeInfo() ) + prop->type.GetTypeInfo()->AddRefInternal(); + } + + // Any child funcdefs must also be copied to the template instance (with adjustments in case of template subtypes) + for (n = 0; n < templateType->childFuncDefs.GetLength(); n++) + { + asCFuncdefType *funcdef = GenerateNewTemplateFuncdef(templateType, ot, templateType->childFuncDefs[n]); + funcdef->parentClass = ot; + ot->childFuncDefs.PushLast(funcdef); + } + + return ot; +} + +// interface +asILockableSharedBool *asCScriptEngine::GetWeakRefFlagOfScriptObject(void *obj, const asITypeInfo *type) const +{ + // Make sure it is not a null pointer + if( obj == 0 || type == 0 ) return 0; + + const asCObjectType *objType = static_cast(type); + asILockableSharedBool *dest = 0; + if( objType->beh.getWeakRefFlag ) + { + // Call the getweakrefflag behaviour + dest = reinterpret_cast(CallObjectMethodRetPtr(obj, objType->beh.getWeakRefFlag)); + } + return dest; +} + +// internal +// orig is the parameter type that is to be replaced +// tmpl is the registered template. Used to find which subtype is being replaced +// ot is the new template instance that is being created. Used to find the target type +asCDataType asCScriptEngine::DetermineTypeForTemplate(const asCDataType &orig, asCObjectType *tmpl, asCObjectType *ot) +{ + asCDataType dt; + if( orig.GetTypeInfo() && (orig.GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE) ) + { + bool found = false; + for( asUINT n = 0; n < tmpl->templateSubTypes.GetLength(); n++ ) + { + if( orig.GetTypeInfo() == tmpl->templateSubTypes[n].GetTypeInfo() ) + { + found = true; + dt = ot->templateSubTypes[n]; + if( orig.IsObjectHandle() && !ot->templateSubTypes[n].IsObjectHandle() ) + { + dt.MakeHandle(true, true); + asASSERT(dt.IsObjectHandle()); + if( orig.IsHandleToConst() ) + dt.MakeHandleToConst(true); + dt.MakeReference(orig.IsReference()); + dt.MakeReadOnly(orig.IsReadOnly()); + } + else + { + // The target type is a handle, then check if the application + // wants this handle to be to a const object. This is done by + // flagging the type with 'if_handle_then_const' in the declaration. + if (dt.IsObjectHandle() && orig.HasIfHandleThenConst()) + dt.MakeHandleToConst(true); + + dt.MakeReference(orig.IsReference()); + dt.MakeReadOnly(ot->templateSubTypes[n].IsReadOnly() || orig.IsReadOnly()); + } + break; + } + } + asASSERT( found ); + UNUSED_VAR( found ); + } + else if( orig.GetTypeInfo() == tmpl ) + { + if( orig.IsObjectHandle() ) + dt = asCDataType::CreateObjectHandle(ot, false); + else + dt = asCDataType::CreateType(ot, false); + + dt.MakeReference(orig.IsReference()); + dt.MakeReadOnly(orig.IsReadOnly()); + } + else if( orig.GetTypeInfo() && (orig.GetTypeInfo()->flags & asOBJ_TEMPLATE) ) + { + // The type is itself a template, so it is necessary to find the correct template instance type + asCArray tmplSubTypes; + asCObjectType *origType = CastToObjectType(orig.GetTypeInfo()); + bool needInstance = true; + + // Find the matching replacements for the subtypes + for( asUINT n = 0; n < origType->templateSubTypes.GetLength(); n++ ) + { + if( origType->templateSubTypes[n].GetTypeInfo() == 0 || + !(origType->templateSubTypes[n].GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE) ) + { + // The template is already an instance so we shouldn't attempt to create another instance + needInstance = false; + break; + } + + for( asUINT m = 0; m < tmpl->templateSubTypes.GetLength(); m++ ) + if( origType->templateSubTypes[n].GetTypeInfo() == tmpl->templateSubTypes[m].GetTypeInfo() ) + tmplSubTypes.PushLast(ot->templateSubTypes[m]); + + if( tmplSubTypes.GetLength() != n+1 ) + { + asASSERT( false ); + return orig; + } + } + + asCObjectType *ntype = origType; + if( needInstance ) + { + // Always find the original template type when creating a new template instance otherwise the + // generation will fail since it will attempt to create factory stubs when they already exists, etc + for( asUINT n = 0; n < registeredTemplateTypes.GetLength(); n++ ) + if( registeredTemplateTypes[n]->name == origType->name ) + { + origType = registeredTemplateTypes[n]; + break; + } + + ntype = GetTemplateInstanceType(origType, tmplSubTypes, ot->module); + if( ntype == 0 ) + { + // It not possible to instantiate the subtype + asASSERT( false ); + ntype = tmpl; + } + } + + if( orig.IsObjectHandle() ) + dt = asCDataType::CreateObjectHandle(ntype, false); + else + dt = asCDataType::CreateType(ntype, false); + + dt.MakeReference(orig.IsReference()); + dt.MakeReadOnly(orig.IsReadOnly()); + } + else + dt = orig; + + return dt; +} + +// internal +asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *templateType, asCObjectType *ot, int factoryId) +{ + asCScriptFunction *factory = scriptFunctions[factoryId]; + + // By first instantiating the function as a dummy and then changing it to be a script function + // I avoid having it added to the garbage collector. As it is known that this object will stay + // alive until the template instance is no longer used there is no need to have the GC check + // this function all the time. + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_DUMMY); + if( func == 0 ) + { + // Out of memory + return 0; + } + + func->funcType = asFUNC_SCRIPT; + func->AllocateScriptFunctionData(); + func->id = GetNextScriptFunctionId(); + AddScriptFunction(func); + + func->isShared = true; + if( templateType->flags & asOBJ_REF ) + { + func->name = "$fact"; + func->returnType = asCDataType::CreateObjectHandle(ot, false); + } + else + { + func->name = "$beh0"; + func->returnType = factory->returnType; // constructors return nothing + func->objectType = ot; + func->objectType->AddRefInternal(); + } + + // Skip the first parameter as this is the object type pointer that the stub will add + func->parameterTypes.SetLength(factory->parameterTypes.GetLength()-1); + func->parameterNames.SetLength(factory->parameterNames.GetLength()-1); + func->inOutFlags.SetLength(factory->inOutFlags.GetLength()-1); + func->defaultArgs.SetLength(factory->defaultArgs.GetLength()-1); + for( asUINT p = 1; p < factory->parameterTypes.GetLength(); p++ ) + { + func->parameterTypes[p-1] = factory->parameterTypes[p]; + func->parameterNames[p-1] = factory->parameterNames[p]; + func->inOutFlags[p-1] = factory->inOutFlags[p]; + func->defaultArgs[p-1] = factory->defaultArgs[p] ? asNEW(asCString)(*factory->defaultArgs[p]) : 0; + } + func->scriptData->objVariablesOnHeap = 0; + + // Generate the bytecode for the factory stub + asUINT bcLength = asBCTypeSize[asBCInfo[asBC_OBJTYPE].type] + + asBCTypeSize[asBCInfo[asBC_CALLSYS].type] + + asBCTypeSize[asBCInfo[asBC_RET].type]; + + if( ep.includeJitInstructions ) + bcLength += asBCTypeSize[asBCInfo[asBC_JitEntry].type]; + if( templateType->flags & asOBJ_VALUE ) + bcLength += asBCTypeSize[asBCInfo[asBC_SwapPtr].type]; + + func->scriptData->byteCode.SetLength(bcLength); + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + + if( ep.includeJitInstructions ) + { + *(asBYTE*)bc = asBC_JitEntry; + *(asPWORD*)(bc+1) = 0; + bc += asBCTypeSize[asBCInfo[asBC_JitEntry].type]; + } + + *(asBYTE*)bc = asBC_OBJTYPE; + *(asPWORD*)(bc+1) = (asPWORD)ot; + bc += asBCTypeSize[asBCInfo[asBC_OBJTYPE].type]; + if( templateType->flags & asOBJ_VALUE ) + { + // Swap the object pointer with the object type + *(asBYTE*)bc = asBC_SwapPtr; + bc += asBCTypeSize[asBCInfo[asBC_SwapPtr].type]; + } + *(asBYTE*)bc = asBC_CALLSYS; + *(asDWORD*)(bc+1) = factoryId; + bc += asBCTypeSize[asBCInfo[asBC_CALLSYS].type]; + *(asBYTE*)bc = asBC_RET; + *(((asWORD*)bc)+1) = (asWORD)func->GetSpaceNeededForArguments() + (func->objectType ? AS_PTR_SIZE : 0); + + func->AddReferences(); + func->scriptData->stackNeeded = AS_PTR_SIZE; + + // Tell the virtual machine not to clean up the object on exception + func->dontCleanUpOnException = true; + + func->JITCompile(); + + // Need to translate the list pattern too so the VM and compiler will know the correct type of the members + if( factory->listPattern ) + { + asSListPatternNode *n = factory->listPattern; + asSListPatternNode *last = 0; + while( n ) + { + asSListPatternNode *newNode = n->Duplicate(); + if( newNode->type == asLPT_TYPE ) + { + asSListPatternDataTypeNode *typeNode = reinterpret_cast(newNode); + typeNode->dataType = DetermineTypeForTemplate(typeNode->dataType, templateType, ot); + } + + if( last ) + last->next = newNode; + else + func->listPattern = newNode; + + last = newNode; + + n = n->next; + } + } + + return func; +} + +bool asCScriptEngine::RequireTypeReplacement(asCDataType &type, asCObjectType *templateType) +{ + if( type.GetTypeInfo() == templateType ) return true; + if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE) ) return true; + if( type.GetTypeInfo() && (type.GetTypeInfo()->flags & asOBJ_TEMPLATE) ) + { + asCObjectType *ot = CastToObjectType(type.GetTypeInfo()); + for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) + if( ot->templateSubTypes[n].GetTypeInfo() && + ot->templateSubTypes[n].GetTypeInfo()->flags & asOBJ_TEMPLATE_SUBTYPE ) + return true; + } + + return false; +} + +bool asCScriptEngine::GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *ot, asCScriptFunction *func, asCScriptFunction **newFunc) +{ + bool needNewFunc = false; + if( RequireTypeReplacement(func->returnType, templateType) ) + needNewFunc = true; + else + { + for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) + { + if( RequireTypeReplacement(func->parameterTypes[p], templateType) ) + { + needNewFunc = true; + break; + } + } + } + + if( !needNewFunc ) + return false; + + asCScriptFunction *func2 = asNEW(asCScriptFunction)(this, 0, func->funcType); + if( func2 == 0 ) + { + // Out of memory + return false; + } + + func2->name = func->name; + + func2->returnType = DetermineTypeForTemplate(func->returnType, templateType, ot); + func2->parameterTypes.SetLength(func->parameterTypes.GetLength()); + for (asUINT p = 0; p < func->parameterTypes.GetLength(); p++) + func2->parameterTypes[p] = DetermineTypeForTemplate(func->parameterTypes[p], templateType, ot); + + for (asUINT n = 0; n < func->defaultArgs.GetLength(); n++) + if (func->defaultArgs[n]) + func2->defaultArgs.PushLast(asNEW(asCString)(*func->defaultArgs[n])); + else + func2->defaultArgs.PushLast(0); + + // TODO: template: Must be careful when instantiating templates for garbage collected types + // If the template hasn't been registered with the behaviours, it shouldn't + // permit instantiation of garbage collected types that in turn may refer to + // this instance. + + func2->parameterNames = func->parameterNames; + func2->inOutFlags = func->inOutFlags; + func2->isReadOnly = func->isReadOnly; + func2->objectType = ot; + func2->objectType->AddRefInternal(); + func2->sysFuncIntf = asNEW(asSSystemFunctionInterface)(*func->sysFuncIntf); + + // Adjust the clean up instructions + if( func2->sysFuncIntf->callConv == ICC_GENERIC_FUNC || + func2->sysFuncIntf->callConv == ICC_GENERIC_METHOD ) + PrepareSystemFunctionGeneric(func2, func2->sysFuncIntf, this); + else + PrepareSystemFunction(func2, func2->sysFuncIntf, this); + + func2->id = GetNextScriptFunctionId(); + AddScriptFunction(func2); + + // Return the new function + *newFunc = func2; + + return true; +} + +asCFuncdefType *asCScriptEngine::GenerateNewTemplateFuncdef(asCObjectType *templateType, asCObjectType *ot, asCFuncdefType *func) +{ + asCScriptFunction *func2 = asNEW(asCScriptFunction)(this, 0, func->funcdef->funcType); + if (func2 == 0) + { + // Out of memory + return 0; + } + + func2->name = func->name; + + func2->returnType = DetermineTypeForTemplate(func->funcdef->returnType, templateType, ot); + func2->parameterTypes.SetLength(func->funcdef->parameterTypes.GetLength()); + for (asUINT p = 0; p < func->funcdef->parameterTypes.GetLength(); p++) + func2->parameterTypes[p] = DetermineTypeForTemplate(func->funcdef->parameterTypes[p], templateType, ot); + + // TODO: template: Must be careful when instantiating templates for garbage collected types + // If the template hasn't been registered with the behaviours, it shouldn't + // permit instantiation of garbage collected types that in turn may refer to + // this instance. + + func2->inOutFlags = func->funcdef->inOutFlags; + func2->isReadOnly = func->funcdef->isReadOnly; + asASSERT(func->funcdef->objectType == 0); + asASSERT(func->funcdef->sysFuncIntf == 0); + + func2->id = GetNextScriptFunctionId(); + AddScriptFunction(func2); + + asCFuncdefType *fdt2 = asNEW(asCFuncdefType)(this, func2); + funcDefs.PushLast(fdt2); // don't increase refCount as the constructor already set it to 1 + + // Return the new function + return fdt2; +} + +void asCScriptEngine::CallObjectMethod(void *obj, int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + CallObjectMethod(obj, s->sysFuncIntf, s); +} + +void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s) const +{ +#if defined(__GNUC__) || defined(AS_PSVITA) + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method + // so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; // Same size as the pointer + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void (asCSimpleDummy::*f)() = p.mthd; + (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void (*f)(void *) = (void (*)(void *))(i->func); + f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void (asCSimpleDummy::*f)() = p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void (*f)(void *) = (void (*)(void *))(i->func); + f(obj); + } +#endif +} + +bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + asSSystemFunctionInterface *i = s->sysFuncIntf; + +#if defined(__GNUC__) || defined(AS_PSVITA) + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + bool (*f)(void *) = (bool (*)(void *))(i->func); + return f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + bool (*f)(void *) = (bool (*)(void *))(i->func); + return f(obj); + } +#endif +} + +int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + asSSystemFunctionInterface *i = s->sysFuncIntf; + +#if defined(__GNUC__) || defined(AS_PSVITA) + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(int*)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + int (*f)(void *) = (int (*)(void *))(i->func); + return f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(int*)gen.GetReturnPointer(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + int (*f)(void *) = (int (*)(void *))(i->func); + return f(obj); + } +#endif +} + +void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + asSSystemFunctionInterface *i = s->sysFuncIntf; + +#if defined(__GNUC__) || defined(AS_PSVITA) + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void**)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void *(asCSimpleDummy::*f)() = (void *(asCSimpleDummy::*)())(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void *(*f)(void *) = (void *(*)(void *))(i->func); + return f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void *(asCSimpleDummy::*f)() = (void *(asCSimpleDummy::*)())p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void **)gen.GetReturnPointer(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void *(*f)(void *) = (void *(*)(void *))(i->func); + return f(obj); + } +#endif +} + +void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const +{ + asASSERT( obj != 0 ); + asASSERT( func != 0 ); + asSSystemFunctionInterface *i = func->sysFuncIntf; + +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { +#if defined(__GNUC__) || defined(AS_PSVITA) + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(param1); +#else + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(param1); +#endif + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), func, obj, reinterpret_cast(¶m1)); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void **)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_CDECL_OBJLAST ) + { + void *(*f)(int, void *) = (void *(*)(int, void *))(i->func); + return f(param1, obj); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void *(*f)(void *, int) = (void *(*)(void *, int))(i->func); + return f(obj, param1); + } +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + return CallGlobalFunctionRetPtr(s->sysFuncIntf, s); +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(int func, void *param1) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + return CallGlobalFunctionRetPtr(s->sysFuncIntf, s, param1); +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s) const +{ + if( i->callConv == ICC_CDECL ) + { + void *(*f)() = (void *(*)())(i->func); + return f(); + } + else if( i->callConv == ICC_STDCALL ) + { + typedef void *(STDCALL *func_t)(); + func_t f = (func_t)(i->func); + return f(); + } + else + { + asCGeneric gen(const_cast(this), s, 0, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void**)gen.GetReturnPointer(); + } +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s, void *param1) const +{ + if( i->callConv == ICC_CDECL ) + { + void *(*f)(void *) = (void *(*)(void *))(i->func); + return f(param1); + } + else if( i->callConv == ICC_STDCALL ) + { + typedef void *(STDCALL *func_t)(void *); + func_t f = (func_t)(i->func); + return f(param1); + } + else + { + asCGeneric gen(const_cast(this), s, 0, (asDWORD*)¶m1); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void**)gen.GetReturnPointer(); + } +} + +void asCScriptEngine::CallObjectMethod(void *obj, void *param, int func) const +{ + asCScriptFunction *s = scriptFunctions[func]; + asASSERT( s != 0 ); + CallObjectMethod(obj, param, s->sysFuncIntf, s); +} + +void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *i, asCScriptFunction *s) const +{ +#if defined(__GNUC__) || defined(AS_PSVITA) + if( i->callConv == ICC_CDECL_OBJLAST ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param, obj); + } + else if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, (asDWORD*)¶m); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else if( i->callConv == ICC_VIRTUAL_THISCALL || i->callConv == ICC_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method + // so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; // Same size as the pointer + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void (asCSimpleDummy::*f)(void*) = (void (asCSimpleDummy::*)(void*))(p.mthd); + (((asCSimpleDummy*)obj)->*f)(param); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST */ + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(obj, param); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void (asCSimpleDummy::*f)(void *) = (void (asCSimpleDummy::*)(void *))(p.mthd); + obj = (void*)(asPWORD(obj) + i->baseOffset); + (((asCSimpleDummy*)obj)->*f)(param); + } + else +#endif + if( i->callConv == ICC_CDECL_OBJLAST ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param, obj); + } + else if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), s, obj, (asDWORD*)¶m); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(obj, param); + } +#endif +} + +void asCScriptEngine::CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s) const +{ + if( i->callConv == ICC_CDECL ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param1, param2); + } + else if( i->callConv == ICC_STDCALL ) + { + typedef void (STDCALL *func_t)(void *, void *); + func_t f = (func_t)(i->func); + f(param1, param2); + } + else + { + // We must guarantee the order of the arguments which is why we copy them to this + // array. Otherwise the compiler may put them anywhere it likes, or even keep them + // in the registers which causes problem. + void *params[2] = {param1, param2}; + + asCGeneric gen(const_cast(this), s, 0, (asDWORD*)¶ms); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } +} + +bool asCScriptEngine::CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s) const +{ + if( i->callConv == ICC_CDECL ) + { + bool (*f)(void *, void *) = (bool (*)(void *, void *))(i->func); + return f(param1, param2); + } + else if( i->callConv == ICC_STDCALL ) + { + typedef bool (STDCALL *func_t)(void *, void *); + func_t f = (func_t)(i->func); + return f(param1, param2); + } + else + { + // TODO: When simulating a 64bit environment by defining AS_64BIT_PTR on a 32bit platform this code + // fails, because the stack given to asCGeneric is not prepared with two 64bit arguments. + + // We must guarantee the order of the arguments which is why we copy them to this + // array. Otherwise the compiler may put them anywhere it likes, or even keep them + // in the registers which causes problem. + void *params[2] = {param1, param2}; + asCGeneric gen(const_cast(this), s, 0, (asDWORD*)params); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } +} + +void *asCScriptEngine::CallAlloc(const asCObjectType *type) const +{ + // Allocate 4 bytes as the smallest size. Otherwise CallSystemFunction may try to + // copy a DWORD onto a smaller memory block, in case the object type is return in registers. + + // Pad to the next even 4 bytes to avoid asBC_CPY writing outside of allocated buffer for registered POD types + asUINT size = type->size; + if( size & 0x3 ) + size += 4 - (size & 0x3); + +#ifndef WIP_16BYTE_ALIGN +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)userAlloc)(size, __FILE__, __LINE__); +#else + return userAlloc(size); +#endif +#else +#if defined(AS_DEBUG) + return ((asALLOCALIGNEDFUNCDEBUG_t)userAllocAligned)(size, type->alignment, __FILE__, __LINE__); +#else + return userAllocAligned(size, type->alignment); +#endif +#endif +} + +void asCScriptEngine::CallFree(void *obj) const +{ +#ifndef WIP_16BYTE_ALIGN + userFree(obj); +#else + userFreeAligned(obj); +#endif +} + +// interface +int asCScriptEngine::NotifyGarbageCollectorOfNewObject(void *obj, asITypeInfo *type) +{ + return gc.AddScriptObjectToGC(obj, static_cast(type)); +} + +// interface +int asCScriptEngine::GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asITypeInfo **type) +{ + return gc.GetObjectInGC(idx, seqNbr, obj, type); +} + +// interface +int asCScriptEngine::GarbageCollect(asDWORD flags, asUINT iterations) +{ + int r = gc.GarbageCollect(flags, iterations); + + if( r == 0 ) + { + // Delete any modules that have been discarded previously but not + // removed due to being referred to by objects in the garbage collector + DeleteDiscardedModules(); + } + + return r; +} + +// interface +void asCScriptEngine::GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const +{ + gc.GetStatistics(currentSize, totalDestroyed, totalDetected, newObjects, totalNewDestroyed); +} + +// interface +void asCScriptEngine::GCEnumCallback(void *reference) +{ + gc.GCEnumCallback(reference); +} + + +int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const +{ + if( dtIn.IsNullHandle() ) return asTYPEID_VOID; + + if( dtIn.GetTypeInfo() == 0 ) + { + // Primitives have pre-fixed typeIds + switch( dtIn.GetTokenType() ) + { + case ttVoid: return asTYPEID_VOID; + case ttBool: return asTYPEID_BOOL; + case ttInt8: return asTYPEID_INT8; + case ttInt16: return asTYPEID_INT16; + case ttInt: return asTYPEID_INT32; + case ttInt64: return asTYPEID_INT64; + case ttUInt8: return asTYPEID_UINT8; + case ttUInt16: return asTYPEID_UINT16; + case ttUInt: return asTYPEID_UINT32; + case ttUInt64: return asTYPEID_UINT64; + case ttFloat: return asTYPEID_FLOAT; + case ttDouble: return asTYPEID_DOUBLE; + default: + // All types should be covered by the above. The variable type is not really a type + asASSERT(dtIn.GetTokenType() == ttQuestion); + return -1; + } + } + + int typeId = -1; + asCTypeInfo *ot = dtIn.GetTypeInfo(); + asASSERT(ot != &functionBehaviours); + // Object's hold the typeId themselves + typeId = ot->typeId; + + if( typeId == -1 ) + { + ACQUIREEXCLUSIVE(engineRWLock); + // Make sure another thread didn't determine the typeId while we were waiting for the lock + if( ot->typeId == -1 ) + { + typeId = typeIdSeqNbr++; + if( ot->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; + else if( ot->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE; + else if( ot->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this? + else typeId |= asTYPEID_APPOBJECT; + + ot->typeId = typeId; + + mapTypeIdToTypeInfo.Insert(typeId, ot); + } + RELEASEEXCLUSIVE(engineRWLock); + } + + // Add flags according to the requested type + if( dtIn.GetTypeInfo() && !(dtIn.GetTypeInfo()->flags & asOBJ_ASHANDLE) ) + { + // The ASHANDLE types behave like handles, but are really + // value types so the typeId is never returned as a handle + if( dtIn.IsObjectHandle() ) + typeId |= asTYPEID_OBJHANDLE; + if( dtIn.IsHandleToConst() ) + typeId |= asTYPEID_HANDLETOCONST; + } + + return typeId; +} + +asCDataType asCScriptEngine::GetDataTypeFromTypeId(int typeId) const +{ + int baseId = typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR); + + if( typeId <= asTYPEID_DOUBLE ) + { + eTokenType type[] = {ttVoid, ttBool, ttInt8, ttInt16, ttInt, ttInt64, ttUInt8, ttUInt16, ttUInt, ttUInt64, ttFloat, ttDouble}; + return asCDataType::CreatePrimitive(type[typeId], false); + } + + // First check if the typeId is an object type + asCTypeInfo *ot = 0; + ACQUIRESHARED(engineRWLock); + asSMapNode *cursor = 0; + if( mapTypeIdToTypeInfo.MoveTo(&cursor, baseId) ) + ot = mapTypeIdToTypeInfo.GetValue(cursor); + RELEASESHARED(engineRWLock); + + if( ot ) + { + asCDataType dt = asCDataType::CreateType(ot, false); + if( typeId & asTYPEID_OBJHANDLE ) + dt.MakeHandle(true, true); + if( typeId & asTYPEID_HANDLETOCONST ) + dt.MakeHandleToConst(true); + + return dt; + } + + return asCDataType(); +} + +asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId) const +{ + asCDataType dt = GetDataTypeFromTypeId(typeId); + return CastToObjectType(dt.GetTypeInfo()); +} + +void asCScriptEngine::RemoveFromTypeIdMap(asCTypeInfo *type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + asSMapNode *cursor = 0; + mapTypeIdToTypeInfo.MoveFirst(&cursor); + while( cursor ) + { + if(mapTypeIdToTypeInfo.GetValue(cursor) == type ) + { + mapTypeIdToTypeInfo.Erase(cursor); + break; + } + mapTypeIdToTypeInfo.MoveNext(&cursor, cursor); + } + RELEASEEXCLUSIVE(engineRWLock); +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +asITypeInfo *asCScriptEngine::GetObjectTypeByDecl(const char *decl) const +{ + asITypeInfo *ti = GetTypeInfoByDecl(decl); + return CastToObjectType(reinterpret_cast(ti)); +} +#endif + +// interface +asITypeInfo *asCScriptEngine::GetTypeInfoByDecl(const char *decl) const +{ + asCDataType dt; + // This cast is ok, because we are not changing anything in the engine + asCBuilder bld(const_cast(this), 0); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if (r < 0) + return 0; + + return dt.GetTypeInfo(); +} + +// interface +int asCScriptEngine::GetTypeIdByDecl(const char *decl) const +{ + asCDataType dt; + // This cast is ok, because we are not changing anything in the engine + asCBuilder bld(const_cast(this), 0); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if( r < 0 ) + return asINVALID_TYPE; + + return GetTypeIdFromDataType(dt); +} + +// interface +const char *asCScriptEngine::GetTypeDeclaration(int typeId, bool includeNamespace) const +{ + asCDataType dt = GetDataTypeFromTypeId(typeId); + + asCString *tempString = &asCThreadManager::GetLocalData()->string; + *tempString = dt.Format(defaultNamespace, includeNamespace); + + return tempString->AddressOf(); +} + +// interface +int asCScriptEngine::GetSizeOfPrimitiveType(int typeId) const +{ + asCDataType dt = GetDataTypeFromTypeId(typeId); + if( !dt.IsPrimitive() ) return 0; + + return dt.GetSizeInMemoryBytes(); +} + +// interface +int asCScriptEngine::RefCastObject(void *obj, asITypeInfo *fromType, asITypeInfo *toType, void **newPtr, bool useOnlyImplicitCast) +{ + if( newPtr == 0 ) return asINVALID_ARG; + *newPtr = 0; + + if( fromType == 0 || toType == 0 ) return asINVALID_ARG; + + // A null-pointer can always be cast to another type, so it will always be successful + if( obj == 0 ) + return asSUCCESS; + + if( fromType == toType ) + { + *newPtr = obj; + AddRefScriptObject(*newPtr, toType); + return asSUCCESS; + } + + // Check for funcdefs + if ((fromType->GetFlags() & asOBJ_FUNCDEF) && (toType->GetFlags() & asOBJ_FUNCDEF)) + { + asCFuncdefType *fromFunc = CastToFuncdefType(reinterpret_cast(fromType)); + asCFuncdefType *toFunc = CastToFuncdefType(reinterpret_cast(toType)); + + if (fromFunc && toFunc && fromFunc->funcdef->IsSignatureExceptNameEqual(toFunc->funcdef)) + { + *newPtr = obj; + AddRefScriptObject(*newPtr, toType); + return asSUCCESS; + } + + return asSUCCESS; + } + + // Look for ref cast behaviours + asCScriptFunction *universalCastFunc = 0; + asCObjectType *from = reinterpret_cast(fromType); + for( asUINT n = 0; n < from->methods.GetLength(); n++ ) + { + asCScriptFunction *func = scriptFunctions[from->methods[n]]; + if( func->name == "opImplCast" || + (!useOnlyImplicitCast && func->name == "opCast") ) + { + if( func->returnType.GetTypeInfo() == toType ) + { + *newPtr = CallObjectMethodRetPtr(obj, func->id); + // The ref cast behaviour returns a handle with incremented + // ref counter, so there is no need to call AddRef explicitly + // unless the function is registered with autohandle + if( func->sysFuncIntf->returnAutoHandle ) + AddRefScriptObject(*newPtr, toType); + return asSUCCESS; + } + else + { + asASSERT( func->returnType.GetTokenType() == ttVoid && + func->parameterTypes.GetLength() == 1 && + func->parameterTypes[0].GetTokenType() == ttQuestion ); + universalCastFunc = func; + } + } + } + + // One last chance if the object has a void opCast(?&out) behaviour + if( universalCastFunc ) + { + // TODO: Add proper error handling + asIScriptContext *ctx = RequestContext(); + ctx->Prepare(universalCastFunc); + ctx->SetObject(obj); + ctx->SetArgVarType(0, newPtr, toType->GetTypeId() | asTYPEID_OBJHANDLE); + ctx->Execute(); + ReturnContext(ctx); + + // The opCast(?&out) method already incremented the + // refCount so there is no need to do it manually + return asSUCCESS; + } + + // For script classes and interfaces there is a quick route + if( (fromType->GetFlags() & asOBJ_SCRIPT_OBJECT) && (toType->GetFlags() & asOBJ_SCRIPT_OBJECT) ) + { + if( fromType == toType ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + + // Up casts to base class or interface can be done implicitly + if( fromType->DerivesFrom(toType) || + fromType->Implements(toType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + // Down casts to derived class or from interface can only be done explicitly + if( !useOnlyImplicitCast ) + { + if( toType->Implements(fromType) || + toType->DerivesFrom(fromType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + + // Get the true type of the object so the explicit cast can evaluate all possibilities + asITypeInfo *trueType = reinterpret_cast(obj)->GetObjectType(); + if( trueType->DerivesFrom(toType) || + trueType->Implements(toType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + } + } + + // The cast is not available, but it is still a success + return asSUCCESS; +} + +// interface +void *asCScriptEngine::CreateScriptObject(const asITypeInfo *type) +{ + if( type == 0 ) return 0; + + asCObjectType *objType = const_cast(reinterpret_cast(type)); + void *ptr = 0; + + // Check that there is a default factory for ref types + if( objType->beh.factory == 0 && (objType->flags & asOBJ_REF) ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_d, "CreateScriptObject", asNO_FUNCTION); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return 0; + } + + // Construct the object + if( objType->flags & asOBJ_SCRIPT_OBJECT ) + { + // Call the script class' default factory with a context + ptr = ScriptObjectFactory(objType, this); + } + else if( (objType->flags & asOBJ_TEMPLATE) && (objType->flags & asOBJ_REF) ) + { + // The registered factory that takes the object type is moved + // to the construct behaviour when the type is instantiated +#ifdef AS_NO_EXCEPTIONS + ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType); +#else + try + { + ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType); + } + catch (...) + { + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + } +#endif + } + else if( objType->flags & asOBJ_REF ) + { + // Call the default factory directly +#ifdef AS_NO_EXCEPTIONS + ptr = CallGlobalFunctionRetPtr(objType->beh.factory); +#else + try + { + ptr = CallGlobalFunctionRetPtr(objType->beh.factory); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + } +#endif + } + else + { + // Make sure there is a default constructor or that it is a POD type + if( objType->beh.construct == 0 && !(objType->flags & asOBJ_POD) ) + { + asCString str; + str.Format(TXT_FAILED_IN_FUNC_s_d, "CreateScriptObject", asNO_FUNCTION); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + return 0; + } + + // Manually allocate the memory, then call the default constructor + ptr = CallAlloc(objType); + int funcIndex = objType->beh.construct; + if (funcIndex) + { + if (objType->flags & asOBJ_TEMPLATE) + { + // Templates of value types create script functions as the constructors + CallScriptObjectMethod(ptr, funcIndex); + } + else + { +#ifdef AS_NO_EXCEPTIONS + CallObjectMethod(ptr, funcIndex); +#else + try + { + CallObjectMethod(ptr, funcIndex); + } + catch (...) + { + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + + // Free the memory + CallFree(ptr); + ptr = 0; + } +#endif + } + } + } + + return ptr; +} + +// internal +int asCScriptEngine::CallScriptObjectMethod(void *obj, int funcId) +{ + asIScriptContext *ctx = 0; + int r = 0; + bool isNested = false; + + // Use nested call in the context if there is an active context + ctx = asGetActiveContext(); + if (ctx) + { + // It may not always be possible to reuse the current context, + // in which case we'll have to create a new one any way. + if (ctx->GetEngine() == this && ctx->PushState() == asSUCCESS) + isNested = true; + else + ctx = 0; + } + + if (ctx == 0) + { + // Request a context from the engine + ctx = RequestContext(); + if (ctx == 0) + { + // TODO: How to best report this failure? + return asERROR; + } + } + + r = ctx->Prepare(scriptFunctions[funcId]); + if (r < 0) + { + if (isNested) + ctx->PopState(); + else + ReturnContext(ctx); + // TODO: How to best report this failure? + return asERROR; + } + + // Set the object + ctx->SetObject(obj); + + for (;;) + { + r = ctx->Execute(); + + // We can't allow this execution to be suspended + // so resume the execution immediately + if (r != asEXECUTION_SUSPENDED) + break; + } + + if (r != asEXECUTION_FINISHED) + { + if (isNested) + { + ctx->PopState(); + + // If the execution was aborted or an exception occurred, + // then we should forward that to the outer execution. + if (r == asEXECUTION_EXCEPTION) + { + // TODO: How to improve this exception + ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL); + } + else if (r == asEXECUTION_ABORTED) + ctx->Abort(); + } + else + ReturnContext(ctx); + + // TODO: How to best report the error? + return asERROR; + } + + if (isNested) + ctx->PopState(); + else + ReturnContext(ctx); + + return asSUCCESS; +} + +// interface +void *asCScriptEngine::CreateUninitializedScriptObject(const asITypeInfo *type) +{ + // This function only works for script classes. Registered types cannot be created this way. + if( type == 0 || !(type->GetFlags() & asOBJ_SCRIPT_OBJECT) ) + return 0; + + asCObjectType *objType = const_cast(reinterpret_cast(type)); + + // Construct the object, but do not call the actual constructor that initializes the members + // The initialization will be done by the application afterwards, e.g. through serialization. + asCScriptObject *obj = reinterpret_cast(CallAlloc(objType)); + + // Pre-initialize the memory so there are no invalid pointers + ScriptObject_ConstructUnitialized(objType, obj); + + return obj; +} + +// interface +void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, const asITypeInfo *type) +{ + if( origObj == 0 || type == 0 ) return 0; + + void *newObj = 0; + + const asCObjectType *ot = reinterpret_cast(type); + // TODO: runtime optimize: Should call copy factory for ref types too + if( ot->beh.copyconstruct ) + { + // Manually allocate the memory, then call the copy constructor + newObj = CallAlloc(ot); +#ifdef AS_NO_EXCEPTIONS + CallObjectMethod(newObj, origObj, ot->beh.copyconstruct); +#else + try + { + CallObjectMethod(newObj, origObj, ot->beh.copyconstruct); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + + // Free the memory + CallFree(newObj); + newObj = 0; + } +#endif + } + else + { + // Allocate the object and then do a value assign + newObj = CreateScriptObject(type); + if( newObj == 0 ) return 0; + + AssignScriptObject(newObj, origObj, type); + } + + return newObj; +} + +// internal +void asCScriptEngine::ConstructScriptObjectCopy(void *mem, void *obj, asCObjectType *type) +{ + if( type == 0 || mem == 0 || obj == 0 ) return; + + // This function is only meant to be used for value types + asASSERT( type->flags & asOBJ_VALUE ); + + // Call the copy constructor if available, else call the default constructor followed by the opAssign + int funcIndex = type->beh.copyconstruct; + if( funcIndex ) + { + CallObjectMethod(mem, obj, funcIndex); + } + else + { + funcIndex = type->beh.construct; + if( funcIndex ) + CallObjectMethod(mem, funcIndex); + + AssignScriptObject(mem, obj, type); + } +} + +// interface +int asCScriptEngine::AssignScriptObject(void *dstObj, void *srcObj, const asITypeInfo *type) +{ + // TODO: Warn about invalid call in message stream + // TODO: Should a script exception be set in case a context is active? + if( type == 0 || dstObj == 0 || srcObj == 0 ) return asINVALID_ARG; + + const asCObjectType *objType = reinterpret_cast(type); + + // If value assign for ref types has been disabled, then don't do anything if the type is a ref type + if( ep.disallowValueAssignForRefType && (objType->flags & asOBJ_REF) && !(objType->flags & asOBJ_SCOPED) ) + return asNOT_SUPPORTED; + + // Must not copy if the opAssign is not available and the object is not a POD object + if( objType->beh.copy ) + { + asCScriptFunction *func = scriptFunctions[objType->beh.copy]; + if( func->funcType == asFUNC_SYSTEM ) + CallObjectMethod(dstObj, srcObj, objType->beh.copy); + else + { + // Call the script class' opAssign method + asASSERT( objType->flags & asOBJ_SCRIPT_OBJECT ); + reinterpret_cast(dstObj)->CopyFrom(reinterpret_cast(srcObj)); + } + } + else if( objType->size && (objType->flags & asOBJ_POD) ) + { + memcpy(dstObj, srcObj, objType->size); + } + + return asSUCCESS; +} + +// interface +void asCScriptEngine::AddRefScriptObject(void *obj, const asITypeInfo *type) +{ + // Make sure it is not a null pointer + if( obj == 0 || type == 0 ) return; + + const asCTypeInfo *ti = static_cast(type); + if (ti->flags & asOBJ_FUNCDEF) + { + CallObjectMethod(obj, functionBehaviours.beh.addref); + } + else + { + asCObjectType *objType = CastToObjectType(const_cast(ti)); + if (objType && objType->beh.addref) + { + // Call the addref behaviour + CallObjectMethod(obj, objType->beh.addref); + } + } +} + +// interface +void asCScriptEngine::ReleaseScriptObject(void *obj, const asITypeInfo *type) +{ + // Make sure it is not a null pointer + if( obj == 0 || type == 0 ) return; + + const asCTypeInfo *ti = static_cast(type); + if (ti->flags & asOBJ_FUNCDEF) + { + CallObjectMethod(obj, functionBehaviours.beh.release); + } + else + { + asCObjectType *objType = CastToObjectType(const_cast(ti)); + if (objType && objType->flags & asOBJ_REF) + { + asASSERT((objType->flags & asOBJ_NOCOUNT) || objType->beh.release); + if (objType->beh.release) + { + // Call the release behaviour + CallObjectMethod(obj, objType->beh.release); + } + } + else if( objType ) + { + // Call the destructor + if (objType->beh.destruct) + CallObjectMethod(obj, objType->beh.destruct); + else if (objType->flags & asOBJ_LIST_PATTERN) + DestroyList((asBYTE*)obj, objType); + + // We'll have to trust that the memory for the object was allocated with CallAlloc. + // This is true if the object was created in the context, or with CreateScriptObject. + + // Then free the memory + CallFree(obj); + } + } +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.30.0, 2014-11-04 +// interface +bool asCScriptEngine::IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const +{ + // if equal, then it is obvious they are compatible + if( objTypeId == handleTypeId ) + return true; + + // Get the actual data types from the type ids + asCDataType objDt = GetDataTypeFromTypeId(objTypeId); + asCDataType hdlDt = GetDataTypeFromTypeId(handleTypeId); + + // A handle to const cannot be passed to a handle that is not referencing a const object + if( objDt.IsHandleToConst() && !hdlDt.IsHandleToConst() ) + return false; + + if( objDt.GetTypeInfo() == hdlDt.GetTypeInfo() ) + { + // The object type is equal + return true; + } + else if( objDt.IsScriptObject() && obj ) + { + // Get the true type from the object instance + asCObjectType *objType = ((asCScriptObject*)obj)->objType; + + // Check if the object implements the interface, or derives from the base class + // This will also return true, if the requested handle type is an exact match for the object type + if( objType->Implements(hdlDt.GetTypeInfo()) || + objType->DerivesFrom(hdlDt.GetTypeInfo()) ) + return true; + } + + return false; +} +#endif + +// interface +int asCScriptEngine::BeginConfigGroup(const char *groupName) +{ + // Make sure the group name doesn't already exist + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + if( configGroups[n]->groupName == groupName ) + return asNAME_TAKEN; + } + + if( currentGroup != &defaultGroup ) + return asNOT_SUPPORTED; + + asCConfigGroup *group = asNEW(asCConfigGroup)(); + if( group == 0 ) + return asOUT_OF_MEMORY; + + group->groupName = groupName; + + configGroups.PushLast(group); + currentGroup = group; + + return 0; +} + +// interface +int asCScriptEngine::EndConfigGroup() +{ + // Raise error if trying to end the default config + if( currentGroup == &defaultGroup ) + return asERROR; + + currentGroup = &defaultGroup; + + return 0; +} + +// interface +int asCScriptEngine::RemoveConfigGroup(const char *groupName) +{ + // It is not allowed to remove a group that is still in use. + + // It would be possible to change the code in such a way that + // the group could be removed even though it was still in use, + // but that would cause severe negative impact on runtime + // performance, since the VM would then have to be able handle + // situations where the types, functions, and global variables + // can be removed at any time. + + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + if( configGroups[n]->groupName == groupName ) + { + asCConfigGroup *group = configGroups[n]; + + // Remove any unused generated template instances + // before verifying if the config group is still in use. + // RemoveTemplateInstanceType() checks if the instance is in use + for( asUINT g = generatedTemplateTypes.GetLength(); g-- > 0; ) + RemoveTemplateInstanceType(generatedTemplateTypes[g]); + + // Make sure the group isn't referenced by anyone + if( group->refCount > 0 ) + return asCONFIG_GROUP_IS_IN_USE; + + // Verify if any objects registered in this group is still alive + if( group->HasLiveObjects() ) + return asCONFIG_GROUP_IS_IN_USE; + + // Remove the group from the list + if( n == configGroups.GetLength() - 1 ) + configGroups.PopLast(); + else + configGroups[n] = configGroups.PopLast(); + + // Remove the configurations registered with this group + group->RemoveConfiguration(this); + + asDELETE(group,asCConfigGroup); + } + } + + return 0; +} + +asCConfigGroup *asCScriptEngine::FindConfigGroupForFunction(int funcId) const +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + // Check global functions + asUINT m; + for( m = 0; m < configGroups[n]->scriptFunctions.GetLength(); m++ ) + { + if( configGroups[n]->scriptFunctions[m]->id == funcId ) + return configGroups[n]; + } + } + + return 0; +} + + +asCConfigGroup *asCScriptEngine::FindConfigGroupForGlobalVar(int gvarId) const +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + for( asUINT m = 0; m < configGroups[n]->globalProps.GetLength(); m++ ) + { + if( int(configGroups[n]->globalProps[m]->id) == gvarId ) + return configGroups[n]; + } + } + + return 0; +} + +asCConfigGroup *asCScriptEngine::FindConfigGroupForTypeInfo(const asCTypeInfo *objType) const +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + for( asUINT m = 0; m < configGroups[n]->types.GetLength(); m++ ) + { + if( configGroups[n]->types[m] == objType ) + return configGroups[n]; + } + } + + return 0; +} + +asCConfigGroup *asCScriptEngine::FindConfigGroupForFuncDef(const asCFuncdefType *funcDef) const +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + asCFuncdefType *f = const_cast(funcDef); + if( configGroups[n]->types.Exists(f) ) + return configGroups[n]; + } + + return 0; +} + +// interface +asDWORD asCScriptEngine::SetDefaultAccessMask(asDWORD defaultMask) +{ + asDWORD old = defaultAccessMask; + defaultAccessMask = defaultMask; + return old; +} + +int asCScriptEngine::GetNextScriptFunctionId() +{ + // This function only returns the next function id that + // should be used. It doesn't update the internal arrays. + if( freeScriptFunctionIds.GetLength() ) + return freeScriptFunctionIds[freeScriptFunctionIds.GetLength()-1]; + + return (int)scriptFunctions.GetLength(); +} + +void asCScriptEngine::AddScriptFunction(asCScriptFunction *func) +{ + // Update the internal arrays with the function id that is now used + if( freeScriptFunctionIds.GetLength() && freeScriptFunctionIds[freeScriptFunctionIds.GetLength()-1] == func->id ) + freeScriptFunctionIds.PopLast(); + + if( asUINT(func->id) == scriptFunctions.GetLength() ) + scriptFunctions.PushLast(func); + else + { + // The slot should be empty or already set with the function, which happens if an existing shared function is reused + asASSERT( scriptFunctions[func->id] == 0 || scriptFunctions[func->id] == func ); + scriptFunctions[func->id] = func; + } +} + +void asCScriptEngine::RemoveScriptFunction(asCScriptFunction *func) +{ + if( func == 0 || func->id < 0 ) return; + int id = func->id & ~FUNC_IMPORTED; + if( func->funcType == asFUNC_IMPORTED ) + { + if( id >= (int)importedFunctions.GetLength() ) return; + + if( importedFunctions[id] ) + { + // Remove the function from the list of script functions + if( id == (int)importedFunctions.GetLength() - 1 ) + { + importedFunctions.PopLast(); + } + else + { + importedFunctions[id] = 0; + freeImportedFunctionIdxs.PushLast(id); + } + } + } + else + { + if( id >= (int)scriptFunctions.GetLength() ) return; + asASSERT( func == scriptFunctions[id] ); + + if( scriptFunctions[id] ) + { + // Remove the function from the list of script functions + if( id == (int)scriptFunctions.GetLength() - 1 ) + { + scriptFunctions.PopLast(); + } + else + { + scriptFunctions[id] = 0; + freeScriptFunctionIds.PushLast(id); + } + + // Is the function used as signature id? + if( func->signatureId == id ) + { + // Remove the signature id + signatureIds.RemoveValue(func); + + // Update all functions using the signature id + int newSigId = 0; + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + { + if( scriptFunctions[n] && scriptFunctions[n]->signatureId == id ) + { + if( newSigId == 0 ) + { + newSigId = scriptFunctions[n]->id; + signatureIds.PushLast(scriptFunctions[n]); + } + + scriptFunctions[n]->signatureId = newSigId; + } + } + } + } + } +} + +// internal +void asCScriptEngine::RemoveFuncdef(asCFuncdefType *funcdef) +{ + funcDefs.RemoveValue(funcdef); +} + +// interface +int asCScriptEngine::RegisterFuncdef(const char *decl) +{ + if( decl == 0 ) return ConfigError(asINVALID_ARG, "RegisterFuncdef", decl, 0); + + // Parse the function declaration + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_FUNCDEF); + if( func == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterFuncdef", decl, 0); + + asCBuilder bld(this, 0); + asCObjectType *parentClass = 0; + int r = bld.ParseFunctionDeclaration(0, decl, func, false, 0, 0, defaultNamespace, 0, &parentClass); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION, "RegisterFuncdef", decl, 0); + } + + // Check name conflicts + r = bld.CheckNameConflict(func->name.AddressOf(), 0, 0, defaultNamespace); + if( r < 0 ) + { + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN, "RegisterFuncdef", decl, 0); + } + + func->id = GetNextScriptFunctionId(); + AddScriptFunction(func); + + asCFuncdefType *fdt = asNEW(asCFuncdefType)(this, func); + funcDefs.PushLast(fdt); // doesn't increase refcount + registeredFuncDefs.PushLast(fdt); // doesn't increase refcount + allRegisteredTypes.Insert(asSNameSpaceNamePair(fdt->nameSpace, fdt->name), fdt); // constructor already set the ref count to 1 + + currentGroup->types.PushLast(fdt); + if (parentClass) + { + parentClass->childFuncDefs.PushLast(fdt); + fdt->parentClass = parentClass; + + // Check if the method restricts that use of the template to value types or reference types + if (parentClass->flags & asOBJ_TEMPLATE) + { + r = SetTemplateRestrictions(parentClass, func, "RegisterFuncdef", decl); + if (r < 0) + return r; + } + } + + // If parameter type from other groups are used, add references + currentGroup->AddReferencesForFunc(this, func); + + // Return the function id as success + return func->id; +} + +// interface +asUINT asCScriptEngine::GetFuncdefCount() const +{ + return asUINT(registeredFuncDefs.GetLength()); +} + +// interface +asITypeInfo *asCScriptEngine::GetFuncdefByIndex(asUINT index) const +{ + if( index >= registeredFuncDefs.GetLength() ) + return 0; + + return registeredFuncDefs[index]; +} + +// internal +asCFuncdefType *asCScriptEngine::FindMatchingFuncdef(asCScriptFunction *func, asCModule *module) +{ + asCFuncdefType *funcDef = func->funcdefType; + + if (funcDef == 0) + { + // Check if there is any matching funcdefs already in the engine that can be reused + for (asUINT n = 0; n < funcDefs.GetLength(); n++) + { + if (funcDefs[n]->funcdef->IsSignatureExceptNameEqual(func)) + { + if (func->isShared && !funcDefs[n]->funcdef->isShared) + continue; + funcDef = funcDefs[n]; + break; + } + } + } + + if (funcDef == 0) + { + // Create a matching funcdef + asCScriptFunction *fd = asNEW(asCScriptFunction)(this, 0, asFUNC_FUNCDEF); + fd->name = func->name; + fd->nameSpace = func->nameSpace; + fd->isShared = func->isShared; + + fd->returnType = func->returnType; + fd->parameterTypes = func->parameterTypes; + fd->inOutFlags = func->inOutFlags; + + funcDef = asNEW(asCFuncdefType)(this, fd); + funcDefs.PushLast(funcDef); // doesn't increase the refCount + + fd->id = GetNextScriptFunctionId(); + AddScriptFunction(fd); + + if (module) + { + // Add the new funcdef to the module so it will + // be available when saving the bytecode + funcDef->module = module; + module->funcDefs.PushLast(funcDef); // the refCount was already accounted for in the constructor + } + + // Observe, if the funcdef is created without informing a module a reference will be stored in the + // engine's funcDefs array, but it will not be owned by any module. This means that it will live on + // until the engine is released. + } + + if (funcDef && module && funcDef->module && funcDef->module != module) + { + // Unless this is a registered funcDef the returned funcDef must + // be stored as part of the module for saving/loading bytecode + if (!module->funcDefs.Exists(funcDef)) + { + module->funcDefs.PushLast(funcDef); + funcDef->AddRefInternal(); + } + else + { + asASSERT(funcDef->IsShared()); + } + } + + return funcDef; +} + +// interface +// TODO: typedef: Accept complex types for the typedefs +int asCScriptEngine::RegisterTypedef(const char *type, const char *decl) +{ + if( type == 0 ) return ConfigError(asINVALID_NAME, "RegisterTypedef", type, decl); + + // Verify if the name has been registered as a type already + // TODO: Must check against registered funcdefs too + if( GetRegisteredType(type, defaultNamespace) ) + // Let the application recover from this error, for example if the same typedef is registered twice + return asALREADY_REGISTERED; + + // Grab the data type + size_t tokenLen; + eTokenType token; + asCDataType dataType; + + // Create the data type + token = tok.GetToken(decl, strlen(decl), &tokenLen); + switch(token) + { + case ttBool: + case ttInt: + case ttInt8: + case ttInt16: + case ttInt64: + case ttUInt: + case ttUInt8: + case ttUInt16: + case ttUInt64: + case ttFloat: + case ttDouble: + if( strlen(decl) != tokenLen ) + { + return ConfigError(asINVALID_TYPE, "RegisterTypedef", type, decl); + } + break; + + default: + return ConfigError(asINVALID_TYPE, "RegisterTypedef", type, decl); + } + + dataType = asCDataType::CreatePrimitive(token, false); + + // Make sure the name is not a reserved keyword + token = tok.GetToken(type, strlen(type), &tokenLen); + if( token != ttIdentifier || strlen(type) != tokenLen ) + return ConfigError(asINVALID_NAME, "RegisterTypedef", type, decl); + + asCBuilder bld(this, 0); + int r = bld.CheckNameConflict(type, 0, 0, defaultNamespace); + if( r < 0 ) + return ConfigError(asNAME_TAKEN, "RegisterTypedef", type, decl); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Put the data type in the list + asCTypedefType *td = asNEW(asCTypedefType)(this); + if( td == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterTypedef", type, decl); + + td->flags = asOBJ_TYPEDEF; + td->size = dataType.GetSizeInMemoryBytes(); + td->name = type; + td->nameSpace = defaultNamespace; + td->aliasForType = dataType; + + allRegisteredTypes.Insert(asSNameSpaceNamePair(td->nameSpace, td->name), td); + registeredTypeDefs.PushLast(td); + + currentGroup->types.PushLast(td); + + return asSUCCESS; +} + +// interface +asUINT asCScriptEngine::GetTypedefCount() const +{ + return asUINT(registeredTypeDefs.GetLength()); +} + +// interface +asITypeInfo *asCScriptEngine::GetTypedefByIndex(asUINT index) const +{ + if( index >= registeredTypeDefs.GetLength() ) + return 0; + + return registeredTypeDefs[index]; +} + +// interface +int asCScriptEngine::RegisterEnum(const char *name) +{ + // Check the name + if( NULL == name ) + return ConfigError(asINVALID_NAME, "RegisterEnum", name, 0); + + // Verify if the name has been registered as a type already + if( GetRegisteredType(name, defaultNamespace) ) + return asALREADY_REGISTERED; + + // Use builder to parse the datatype + asCDataType dt; + asCBuilder bld(this, 0); + bool oldMsgCallback = msgCallback; msgCallback = false; + int r = bld.ParseDataType(name, &dt, defaultNamespace); + msgCallback = oldMsgCallback; + if( r >= 0 ) + { + // If it is not in the defaultNamespace then the type was successfully parsed because + // it is declared in a parent namespace which shouldn't be treated as an error + if( dt.GetTypeInfo() && dt.GetTypeInfo()->nameSpace == defaultNamespace ) + return ConfigError(asERROR, "RegisterEnum", name, 0); + } + + // Make sure the name is not a reserved keyword + size_t tokenLen; + int token = tok.GetToken(name, strlen(name), &tokenLen); + if( token != ttIdentifier || strlen(name) != tokenLen ) + return ConfigError(asINVALID_NAME, "RegisterEnum", name, 0); + + r = bld.CheckNameConflict(name, 0, 0, defaultNamespace); + if( r < 0 ) + return ConfigError(asNAME_TAKEN, "RegisterEnum", name, 0); + + asCEnumType *st = asNEW(asCEnumType)(this); + if( st == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterEnum", name, 0); + + asCDataType dataType; + dataType.CreatePrimitive(ttInt, false); + + st->flags = asOBJ_ENUM | asOBJ_SHARED; + st->size = 4; + st->name = name; + st->nameSpace = defaultNamespace; + + allRegisteredTypes.Insert(asSNameSpaceNamePair(st->nameSpace, st->name), st); + registeredEnums.PushLast(st); + + currentGroup->types.PushLast(st); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterEnumValue(const char *typeName, const char *valueName, int value) +{ + // Verify that the correct config group is used + if( currentGroup->FindType(typeName) == 0 ) + return ConfigError(asWRONG_CONFIG_GROUP, "RegisterEnumValue", typeName, valueName); + + asCDataType dt; + int r; + asCBuilder bld(this, 0); + r = bld.ParseDataType(typeName, &dt, defaultNamespace); + if( r < 0 ) + return ConfigError(r, "RegisterEnumValue", typeName, valueName); + + // Store the enum value + asCEnumType *ot = CastToEnumType(dt.GetTypeInfo()); + if( ot == 0 ) + return ConfigError(asINVALID_TYPE, "RegisterEnumValue", typeName, valueName); + + if( NULL == valueName ) + return ConfigError(asINVALID_NAME, "RegisterEnumValue", typeName, valueName); + + asUINT tokenLen = 0; + asETokenClass tokenClass = ParseToken(valueName, 0, &tokenLen); + if( tokenClass != asTC_IDENTIFIER || tokenLen != strlen(valueName) ) + return ConfigError(asINVALID_NAME, "RegisterEnumValue", typeName, valueName); + + for( unsigned int n = 0; n < ot->enumValues.GetLength(); n++ ) + { + if( ot->enumValues[n]->name == valueName ) + return ConfigError(asALREADY_REGISTERED, "RegisterEnumValue", typeName, valueName); + } + + asSEnumValue *e = asNEW(asSEnumValue); + if( e == 0 ) + return ConfigError(asOUT_OF_MEMORY, "RegisterEnumValue", typeName, valueName); + + e->name = valueName; + e->value = value; + + ot->enumValues.PushLast(e); + + return asSUCCESS; +} + +// interface +asUINT asCScriptEngine::GetEnumCount() const +{ + return registeredEnums.GetLength(); +} + +// interface +asITypeInfo *asCScriptEngine::GetEnumByIndex(asUINT index) const +{ + if( index >= registeredEnums.GetLength() ) + return 0; + + return registeredEnums[index]; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +int asCScriptEngine::GetEnumValueCount(int enumTypeId) const +{ + asITypeInfo *ti = GetTypeInfoById(enumTypeId); + asCEnumType *e = CastToEnumType(reinterpret_cast(ti)); + if (e == 0) + return asINVALID_TYPE; + + return e->GetEnumValueCount(); +} +#endif + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +const char *asCScriptEngine::GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const +{ + asITypeInfo *ti = GetTypeInfoById(enumTypeId); + asCEnumType *e = CastToEnumType(reinterpret_cast(ti)); + if (e == 0) + return 0; + + return e->GetEnumValueByIndex(index, outValue); +} +#endif + +// interface +asUINT asCScriptEngine::GetObjectTypeCount() const +{ + return asUINT(registeredObjTypes.GetLength()); +} + +// interface +asITypeInfo *asCScriptEngine::GetObjectTypeByIndex(asUINT index) const +{ + if( index >= registeredObjTypes.GetLength() ) + return 0; + + return registeredObjTypes[index]; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +asITypeInfo *asCScriptEngine::GetObjectTypeByName(const char *name) const +{ + asITypeInfo *ti = GetTypeInfoByName(name); + return CastToObjectType(reinterpret_cast(ti)); +} +#endif + +// interface +asITypeInfo *asCScriptEngine::GetTypeInfoByName(const char *name) const +{ + asSNameSpace *ns = defaultNamespace; + while (ns) + { + // Check the object types + for (asUINT n = 0; n < registeredObjTypes.GetLength(); n++) + { + if (registeredObjTypes[n]->name == name && + registeredObjTypes[n]->nameSpace == ns) + return registeredObjTypes[n]; + } + + // Perhaps it is a template type? In this case + // the returned type will be the generic type + for (asUINT n = 0; n < registeredTemplateTypes.GetLength(); n++) + { + if (registeredTemplateTypes[n]->name == name && + registeredTemplateTypes[n]->nameSpace == ns) + return registeredTemplateTypes[n]; + } + + // Check the enum types + for (asUINT n = 0; n < registeredEnums.GetLength(); n++) + { + if (registeredEnums[n]->name == name && + registeredEnums[n]->nameSpace == ns) + return registeredEnums[n]; + } + + // Check the typedefs + for (asUINT n = 0; n < registeredTypeDefs.GetLength();n++) + { + if (registeredTypeDefs[n]->name == name && + registeredTypeDefs[n]->nameSpace == ns) + return registeredTypeDefs[n]; + } + + // Recursively search parent namespace + ns = GetParentNameSpace(ns); + } + + return 0; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +asITypeInfo *asCScriptEngine::GetObjectTypeById(int typeId) const +{ + asITypeInfo *ti = GetTypeInfoById(typeId); + return CastToObjectType(reinterpret_cast(ti)); +} +#endif + +// interface +asITypeInfo *asCScriptEngine::GetTypeInfoById(int typeId) const +{ + asCDataType dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if (!dt.IsValid()) return 0; + + return dt.GetTypeInfo(); +} + +// interface +asIScriptFunction *asCScriptEngine::GetFunctionById(int funcId) const +{ + return GetScriptFunction(funcId); +} + +#ifdef AS_DEPRECATED +// deprecated since 2.31.0, 2016-01-01 +// interface +asIScriptFunction *asCScriptEngine::GetFuncdefFromTypeId(int typeId) const +{ + asCFuncdefType *t = CastToFuncdefType(GetDataTypeFromTypeId(typeId).GetTypeInfo()); + if (t) + return t->funcdef; + return 0; +} +#endif + +// internal +bool asCScriptEngine::IsTemplateType(const char *name) const +{ + // Only look in the list of template types (not instance types) + for( unsigned int n = 0; n < registeredTemplateTypes.GetLength(); n++ ) + { + asCObjectType *type = registeredTemplateTypes[n]; + if( type && type->name == name ) + return true; + } + + return false; +} + +// internal +int asCScriptEngine::AddConstantString(const char *str, size_t len) +{ + // This is only called when build a script module, so it is + // known that only one thread can enter the function at a time. + asASSERT( isBuilding ); + + // The str may contain null chars, so we cannot use strlen, or strcmp, or strcpy + + // Has the string been registered before? + asSMapNode *cursor = 0; + if (stringToIdMap.MoveTo(&cursor, asCStringPointer(str, len))) + return cursor->value; + + // No match was found, add the string + asCString *cstr = asNEW(asCString)(str, len); + if( cstr ) + { + stringConstants.PushLast(cstr); + int index = (int)stringConstants.GetLength() - 1; + stringToIdMap.Insert(asCStringPointer(cstr), index); + + // The VM currently doesn't handle string ids larger than 65535 + asASSERT(stringConstants.GetLength() <= 65536); + + return index; + } + + return 0; +} + +// internal +const asCString &asCScriptEngine::GetConstantString(int id) +{ + return *stringConstants[id]; +} + +// internal +int asCScriptEngine::GetScriptSectionNameIndex(const char *name) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + // TODO: These names are only released when the engine is freed. The assumption is that + // the same script section names will be reused instead of there always being new + // names. Is this assumption valid? Do we need to add reference counting? + + // Store the script section names for future reference + for( asUINT n = 0; n < scriptSectionNames.GetLength(); n++ ) + { + if( scriptSectionNames[n]->Compare(name) == 0 ) + { + RELEASEEXCLUSIVE(engineRWLock); + return n; + } + } + + asCString *str = asNEW(asCString)(name); + if( str ) + scriptSectionNames.PushLast(str); + int r = int(scriptSectionNames.GetLength()-1); + + RELEASEEXCLUSIVE(engineRWLock); + + return r; +} + +// interface +void asCScriptEngine::SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanEngineFuncs.GetLength(); n++ ) + { + if( cleanEngineFuncs[n].type == type ) + { + cleanEngineFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SEngineClean otc = {type, callback}; + cleanEngineFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +// interface +void asCScriptEngine::SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanModuleFuncs.GetLength(); n++ ) + { + if( cleanModuleFuncs[n].type == type ) + { + cleanModuleFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SModuleClean otc = {type, callback}; + cleanModuleFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +// interface +void asCScriptEngine::SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanContextFuncs.GetLength(); n++ ) + { + if( cleanContextFuncs[n].type == type ) + { + cleanContextFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SContextClean otc = {type, callback}; + cleanContextFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +// interface +void asCScriptEngine::SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanFunctionFuncs.GetLength(); n++ ) + { + if( cleanFunctionFuncs[n].type == type ) + { + cleanFunctionFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SFunctionClean otc = {type, callback}; + cleanFunctionFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +#ifdef AS_DEPRECATED +// Deprecated since 2.31.0, 2015-12-06 +// interface +void asCScriptEngine::SetObjectTypeUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type) +{ + SetTypeInfoUserDataCleanupCallback(callback, type); +} +#endif + +// interface +void asCScriptEngine::SetTypeInfoUserDataCleanupCallback(asCLEANTYPEINFOFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanTypeInfoFuncs.GetLength(); n++ ) + { + if( cleanTypeInfoFuncs[n].type == type ) + { + cleanTypeInfoFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + STypeInfoClean otc = {type, callback}; + cleanTypeInfoFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +// interface +void asCScriptEngine::SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanScriptObjectFuncs.GetLength(); n++ ) + { + if( cleanScriptObjectFuncs[n].type == type ) + { + cleanScriptObjectFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SScriptObjClean soc = {type, callback}; + cleanScriptObjectFuncs.PushLast(soc); + + RELEASEEXCLUSIVE(engineRWLock); +} + +// internal +asCObjectType *asCScriptEngine::GetListPatternType(int listPatternFuncId) +{ + // Get the object type either from the constructor's object for value types + // or from the factory's return type for reference types + asCObjectType *ot = scriptFunctions[listPatternFuncId]->objectType; + if( ot == 0 ) + ot = CastToObjectType(scriptFunctions[listPatternFuncId]->returnType.GetTypeInfo()); + asASSERT( ot ); + + // Check if this object type already has a list pattern type + for( asUINT n = 0; n < listPatternTypes.GetLength(); n++ ) + { + if( listPatternTypes[n]->templateSubTypes[0].GetTypeInfo() == ot ) + return listPatternTypes[n]; + } + + // Create a new list pattern type for the given object type + asCObjectType *lpt = asNEW(asCObjectType)(this); + lpt->templateSubTypes.PushLast(asCDataType::CreateType(ot, false)); + lpt->flags = asOBJ_LIST_PATTERN; + listPatternTypes.PushLast(lpt); + + return lpt; +} + +// internal +void asCScriptEngine::DestroyList(asBYTE *buffer, const asCObjectType *listPatternType) +{ + asASSERT( listPatternType && (listPatternType->flags & asOBJ_LIST_PATTERN) ); + + // Get the list pattern from the listFactory function + // TODO: runtime optimize: Store the used list factory in the listPatternType itself + // TODO: runtime optimize: Keep a flag to indicate if there is really a need to free anything + asCObjectType *ot = CastToObjectType(listPatternType->templateSubTypes[0].GetTypeInfo()); + asCScriptFunction *listFactory = scriptFunctions[ot->beh.listFactory]; + asASSERT( listFactory ); + + asSListPatternNode *node = listFactory->listPattern; + DestroySubList(buffer, node); + + asASSERT( node->type == asLPT_END ); +} + +// internal +void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) +{ + asASSERT( node->type == asLPT_START ); + + int count = 0; + + node = node->next; + while( node ) + { + if( node->type == asLPT_REPEAT || node->type == asLPT_REPEAT_SAME ) + { + // Align the offset to 4 bytes boundary + if( (asPWORD(buffer) & 0x3) ) + buffer += 4 - (asPWORD(buffer) & 0x3); + + // Determine how many times the pattern repeat + count = *(asUINT*)buffer; + buffer += 4; + + if( count == 0 ) + { + // Skip the sub pattern that was expected to be repeated, otherwise + // we'll try to delete things that don't exist in the buffer + node = node->next; + if( node->type == asLPT_START ) + { + int subCount = 1; + do + { + node = node->next; + if( node->type == asLPT_START ) + subCount++; + else if( node->type == asLPT_END ) + subCount--; + } while( subCount > 0 ); + return; + } + } + } + else if( node->type == asLPT_TYPE ) + { + // If we're not in a repeat iteration, then only 1 value should be destroyed + if( count <= 0 ) + count = 1; + + asCDataType dt = reinterpret_cast(node)->dataType; + bool isVarType = dt.GetTokenType() == ttQuestion; + + while( count-- ) + { + if( isVarType ) + { + // Align the offset to 4 bytes boundary + if( (asPWORD(buffer) & 0x3) ) + buffer += 4 - (asPWORD(buffer) & 0x3); + + int typeId = *(int*)buffer; + buffer += 4; + dt = GetDataTypeFromTypeId(typeId); + } + + asCTypeInfo *ti = dt.GetTypeInfo(); + if( ti && (ti->flags & asOBJ_ENUM) == 0 ) + { + // Free all instances of this type + if( ti->flags & asOBJ_VALUE ) + { + asUINT size = ti->GetSize(); + + // Align the offset to 4 bytes boundary + if( size >= 4 && (asPWORD(buffer) & 0x3) ) + buffer += 4 - (asPWORD(buffer) & 0x3); + + asCObjectType *ot = CastToObjectType(ti); + if( ot && ot->beh.destruct ) + { + // Only call the destructor if the object has been created + // We'll assume the object has been created if any byte in + // the memory is different from 0. + // TODO: This is not really correct, as bytes may have been + // modified by the constructor, but then an exception + // thrown aborting the initialization. The engine + // really should be keeping track of which objects has + // been successfully initialized. + + for( asUINT n = 0; n < size; n++ ) + { + if( buffer[n] != 0 ) + { + void *ptr = (void*)buffer; + CallObjectMethod(ptr, ot->beh.destruct); + break; + } + } + } + + // Advance the pointer in the buffer + buffer += size; + } + else + { + // Align the offset to 4 bytes boundary + if( asPWORD(buffer) & 0x3 ) + buffer += 4 - (asPWORD(buffer) & 0x3); + + // Call the release behaviour + void *ptr = *(void**)buffer; + if( ptr ) + ReleaseScriptObject(ptr, ti); + buffer += AS_PTR_SIZE*4; + } + } + else + { + asUINT size = dt.GetSizeInMemoryBytes(); + + // Align the offset to 4 bytes boundary + if( size >= 4 && (asPWORD(buffer) & 0x3) ) + buffer += 4 - (asPWORD(buffer) & 0x3); + + // Advance the buffer + buffer += size; + } + } + } + else if( node->type == asLPT_START ) + { + // If we're not in a repeat iteration, then only 1 value should be destroyed + if( count <= 0 ) + count = 1; + + while( count-- ) + { + asSListPatternNode *subList = node; + DestroySubList(buffer, subList); + + asASSERT( subList->type == asLPT_END ); + + if( count == 0 ) + node = subList; + } + } + else if( node->type == asLPT_END ) + { + return; + } + else + { + asASSERT( false ); + } + + node = node->next; + } +} + +// internal +asSNameSpace *asCScriptEngine::GetParentNameSpace(asSNameSpace *ns) const +{ + if( ns == 0 ) return 0; + if( ns == nameSpaces[0] ) return 0; + + asCString scope = ns->name; + int pos = scope.FindLast("::"); + if( pos >= 0 ) + { + scope = scope.SubString(0, pos); + return FindNameSpace(scope.AddressOf()); + } + + return nameSpaces[0]; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_scriptfunction.cpp b/3rdparty/angelscript/src/as_scriptfunction.cpp new file mode 100644 index 0000000..3474d4a --- /dev/null +++ b/3rdparty/angelscript/src/as_scriptfunction.cpp @@ -0,0 +1,1698 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptfunction.cpp +// +// A container for a compiled script function +// + + + +#include "as_config.h" +#include "as_scriptfunction.h" +#include "as_tokendef.h" +#include "as_scriptengine.h" +#include "as_callfunc.h" +#include "as_bytecode.h" +#include "as_texts.h" +#include "as_scriptnode.h" +#include "as_builder.h" +#include "as_scriptcode.h" + +#include // qsort + +BEGIN_AS_NAMESPACE + +#ifdef AS_MAX_PORTABILITY + +static void ScriptFunction_AddRef_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptFunction_Release_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->Release(); +} + +static void ScriptFunction_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptFunction_SetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptFunction_GetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptFunction_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptFunction_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +static void ScriptFunction_CreateDelegate_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *func = (asCScriptFunction*)gen->GetArgAddress(0); + void *obj = gen->GetArgAddress(1); + gen->SetReturnAddress(CreateDelegate(func, obj)); +} + +// TODO: operator== +/*static void ScriptFunction_opEquals_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *funcSelf = (asCScriptFunction*)gen->GetObject(); + asCScriptFunction *funcOther = (asCScriptFunction*)gen->GetArgAddress(0); + *(bool*)gen->GetAddressOfReturnLocation() = *funcSelf == *funcOther; +} +*/ + +#endif + + +void RegisterScriptFunction(asCScriptEngine *engine) +{ + // Register the gc behaviours for the script functions + int r = 0; + UNUSED_VAR(r); // It is only used in debug mode + engine->functionBehaviours.engine = engine; + engine->functionBehaviours.flags = asOBJ_REF | asOBJ_GC; + engine->functionBehaviours.name = "$func"; +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptFunction,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptFunction,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptFunction,GetRefCount), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptFunction,SetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptFunction,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptFunction,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptFunction,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + // TODO: Need some way to allow the arg type to adapt when the funcdefs are instantiated +// r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asMETHOD(asCScriptFunction,operator==), asCALL_THISCALL); asASSERT( r >= 0 ); +#else + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFunction_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFunction_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptFunction_GetRefCount_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptFunction_SetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptFunction_GetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptFunction_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptFunction_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); +// r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asFUNCTION(ScriptFunction_opEquals_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif + + // Register the builtin function for creating delegates + // This function returns a handle to the delegate, but since the type is not known at this time it is + // registered to return a void then the return type is changed manually to the builtin function type + // The name of the function is an invalid identifier so it cannot be invoked accidentally from the script +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterGlobalFunction("void f(int &in, int &in)", asFUNCTION(CreateDelegate), asCALL_CDECL); asASSERT( r >= 0 ); +#else + r = engine->RegisterGlobalFunction("void f(int &in, int &in)", asFUNCTION(ScriptFunction_CreateDelegate_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif + + // Rename the function so that it cannot be called manually by the script + int idx = engine->registeredGlobalFuncs.GetIndex(engine->scriptFunctions[r]); + engine->registeredGlobalFuncs.Erase(idx); + engine->scriptFunctions[r]->name = DELEGATE_FACTORY; + engine->registeredGlobalFuncs.Put(engine->scriptFunctions[r]); + + // Change the return type so the VM will know the function really returns a handle + engine->scriptFunctions[r]->returnType = asCDataType::CreateType(&engine->functionBehaviours, false); + engine->scriptFunctions[r]->returnType.MakeHandle(true); +} + +asCScriptFunction *CreateDelegate(asCScriptFunction *func, void *obj) +{ + if( func == 0 || obj == 0 ) + { + // TODO: delegate: Should set script exception + return 0; + } + + // Create an instance of a asCScriptFunction with the type asFUNC_DELEGATE + // The delegate shouldn't have a function id and is not added to the engine->scriptFunctions + asCScriptFunction *delegate = asNEW(asCScriptFunction)(static_cast(func->GetEngine()), 0, asFUNC_DELEGATE); + if( delegate ) + delegate->MakeDelegate(func, obj); + + return delegate; +} + +// internal +void asCScriptFunction::MakeDelegate(asCScriptFunction *func, void *obj) +{ + // Increase the reference of the function and object + func->AddRef(); + funcForDelegate = func; + + func->GetEngine()->AddRefScriptObject(obj, func->GetObjectType()); + objForDelegate = obj; + + // The return type and parameters are copied from the delegated method to this object + // TODO: optimize: Do we really need to copy? Whenever requested the delegate can simply return the delegated methods' info directly + parameterTypes = func->parameterTypes; + returnType = func->returnType; + inOutFlags = func->inOutFlags; + + // The delegate doesn't own the parameters as it will only forward them to the real method + // so the exception handler must not clean up the parameters for the delegate + dontCleanUpOnException = true; +} + +// interface +void *asCScriptFunction::GetAuxiliary() const +{ + if (sysFuncIntf) + return sysFuncIntf->auxiliary; + + return 0; +} + +// interface +void *asCScriptFunction::GetDelegateObject() const +{ + return objForDelegate; +} + +// interface +asITypeInfo *asCScriptFunction::GetDelegateObjectType() const +{ + if( objForDelegate == 0 || funcForDelegate == 0 ) + return 0; + + return funcForDelegate->objectType; +} + +// interface +asIScriptFunction *asCScriptFunction::GetDelegateFunction() const +{ + return funcForDelegate; +} + +// TODO: operator== +/* +// internal +bool asCScriptFunction::operator==(const asCScriptFunction &other) const +{ + if( this == &other ) return true; + + if( this->funcType == asFUNC_DELEGATE && other.funcType == asFUNC_DELEGATE ) + { + if( this->objForDelegate == other.objForDelegate && + this->funcForDelegate == other.funcForDelegate ) + return true; + } + + return false; +} +*/ + +// internal +int asCScriptFunction::RegisterListPattern(const char *decl, asCScriptNode *listNodes) +{ + if( listNodes == 0 ) + return asINVALID_ARG; + + // Build the representation of the list pattern from the script nodes + asSListPatternNode *node; + listPattern = asNEW(asSListPatternNode)(asLPT_START); + node = listPattern; + + // Recursively parse the child + int r = ParseListPattern(node, decl, listNodes); + + node->next = asNEW(asSListPatternNode)(asLPT_END); + + return r; +} + +// internal +int asCScriptFunction::ParseListPattern(asSListPatternNode *&target, const char *decl, asCScriptNode *listNodes) +{ + asSListPatternNode *node = target; + + listNodes = listNodes->firstChild; + while( listNodes ) + { + if( listNodes->nodeType == snIdentifier ) + { + asCString token(&decl[listNodes->tokenPos], listNodes->tokenLength); + if( token == "repeat" ) + { + node->next = asNEW(asSListPatternNode)(asLPT_REPEAT); + node = node->next; + } + else if( token == "repeat_same" ) + { + // TODO: list: Should make sure this is a sub-list + node->next = asNEW(asSListPatternNode)(asLPT_REPEAT_SAME); + node = node->next; + } + else + { + // Shouldn't happen as the parser already reported the error + asASSERT(false); + } + } + else if( listNodes->nodeType == snDataType ) + { + asCDataType dt; + asCBuilder builder(engine, 0); + asCScriptCode code; + code.SetCode("", decl, 0, false); + dt = builder.CreateDataTypeFromNode(listNodes, &code, engine->defaultNamespace, false, CastToObjectType(returnType.GetTypeInfo())); + + node->next = asNEW(asSListPatternDataTypeNode)(dt); + node = node->next; + } + else if( listNodes->nodeType == snListPattern ) + { + node->next = asNEW(asSListPatternNode)(asLPT_START); + node = node->next; + + // Recursively parse the child + int r = ParseListPattern(node, decl, listNodes); + if( r < 0 ) + return r; + + node->next = asNEW(asSListPatternNode)(asLPT_END); + node = node->next; + } + else + { + // Unexpected token in the list, the parser shouldn't have allowed + asASSERT( false ); + return -1; + } + + listNodes = listNodes->next; + } + + target = node; + return 0; +} + +// internal +asCScriptFunction::asCScriptFunction(asCScriptEngine *engine, asCModule *mod, asEFuncType _funcType) +{ + funcType = _funcType; + if( funcType == asFUNC_DELEGATE ) + { + // Delegates behave like object instances, rather than script code + externalRefCount.set(1); + internalRefCount.set(0); + } + else + { + internalRefCount.set(1); + externalRefCount.set(0); + } + + this->engine = engine; + this->scriptData = 0; + module = mod; + objectType = 0; + name = ""; + isReadOnly = false; + isPrivate = false; + isProtected = false; + isFinal = false; + isOverride = false; + sysFuncIntf = 0; + signatureId = 0; + dontCleanUpOnException = false; + vfTableIdx = -1; + gcFlag = false; + userData = 0; + id = 0; + accessMask = 0xFFFFFFFF; + isShared = false; + nameSpace = engine->nameSpaces[0]; + objForDelegate = 0; + funcForDelegate = 0; + listPattern = 0; + funcdefType = 0; + + if( funcType == asFUNC_SCRIPT ) + AllocateScriptFunctionData(); + + // Notify the GC of delegates + if( funcType == asFUNC_DELEGATE ) + engine->gc.AddScriptObjectToGC(this, &engine->functionBehaviours); +} + +void asCScriptFunction::AllocateScriptFunctionData() +{ + if( scriptData ) return; + + scriptData = asNEW(ScriptFunctionData); + + scriptData->stackNeeded = 0; + scriptData->variableSpace = 0; + scriptData->scriptSectionIdx = -1; + scriptData->declaredAt = 0; + scriptData->jitFunction = 0; +} + +void asCScriptFunction::DeallocateScriptFunctionData() +{ + if( !scriptData ) return; + + for( asUINT n = 0; n < scriptData->variables.GetLength(); n++ ) + asDELETE(scriptData->variables[n],asSScriptVariable); + scriptData->variables.SetLength(0); + + asDELETE(scriptData, ScriptFunctionData); + scriptData = 0; +} + +// internal +asCScriptFunction::~asCScriptFunction() +{ + // Dummy functions that are allocated on the stack are not reference counted + asASSERT( funcType == asFUNC_DUMMY || + (externalRefCount.get() == 0 && internalRefCount.get() == 0) ); + + // Remove the script function from the engine's scriptFunctions array here + // Don't remove it before, because there may still be functions referring to it + // by index until now. If it was removed in DestroyInternal, those would not + // be able to release the refcount, thus causing memory leak. + if( engine && id != 0 && funcType != asFUNC_DUMMY ) + engine->RemoveScriptFunction(this); + + // If the engine pointer is 0, then DestroyInternal has already been called and there is nothing more to do + if( engine == 0 ) return; + + DestroyInternal(); + + // Finally set the engine pointer to 0 because it must not be accessed again + engine = 0; +} + +// internal +void asCScriptFunction::DestroyHalfCreated() +{ + asASSERT( externalRefCount.get() == 0 && internalRefCount.get() == 1 ); + + // Set the funcType to dummy so the destructor won't complain + funcType = asFUNC_DUMMY; + + // If the bytecode exist remove it before destroying, otherwise it + // will fail when the destructor releases the references as the bytecode + // is not fully constructed. + if( scriptData ) + scriptData->byteCode.SetLength(0); + + asDELETE(this, asCScriptFunction); +} + +// internal +void asCScriptFunction::DestroyInternal() +{ + // Clean up user data + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n+1] ) + { + for( asUINT c = 0; c < engine->cleanFunctionFuncs.GetLength(); c++ ) + if( engine->cleanFunctionFuncs[c].type == userData[n] ) + engine->cleanFunctionFuncs[c].cleanFunc(this); + } + } + userData.SetLength(0); + + // Release all references the function holds to other objects + ReleaseReferences(); + parameterTypes.SetLength(0); + returnType = asCDataType::CreatePrimitive(ttVoid, false); + + for( asUINT p = 0; p < defaultArgs.GetLength(); p++ ) + if( defaultArgs[p] ) + asDELETE(defaultArgs[p], asCString); + defaultArgs.SetLength(0); + + if( sysFuncIntf ) + asDELETE(sysFuncIntf,asSSystemFunctionInterface); + sysFuncIntf = 0; + + if( objectType ) + { + objectType->ReleaseInternal(); + objectType = 0; + } + + DeallocateScriptFunctionData(); + + // Deallocate list pattern data + while( listPattern ) + { + asSListPatternNode *n = listPattern->next; + asDELETE(listPattern, asSListPatternNode); + listPattern = n; + } +} + +// interface +int asCScriptFunction::GetId() const +{ + return id; +} + +// interface +int asCScriptFunction::AddRef() const +{ + gcFlag = false; + return externalRefCount.atomicInc(); +} + +// interface +int asCScriptFunction::Release() const +{ + gcFlag = false; + int r = externalRefCount.atomicDec(); + if( r == 0 && + funcType != asFUNC_DUMMY ) // Dummy functions are allocated on the stack and cannot be deleted + { + // There are no more external references, if there are also no + // internal references then it is time to delete the function + if( internalRefCount.get() == 0 ) + { + // If there are no internal references, then no module is owning the function + // For example if the function was dynamically compiled without adding it to the scope of the module + asASSERT( module == 0 ); + + asDELETE(const_cast(this),asCScriptFunction); + } + } + + return r; +} + +// internal +int asCScriptFunction::AddRefInternal() +{ + return internalRefCount.atomicInc(); +} + +// internal +int asCScriptFunction::ReleaseInternal() +{ + int r = internalRefCount.atomicDec(); + if( r == 0 && + funcType != asFUNC_DUMMY ) + { + // There are no more internal references, if there are also no + // external references then it is time to delete the function + if( externalRefCount.get() == 0 ) + { + asDELETE(const_cast(this),asCScriptFunction); + } + } + + return r; +} + +// interface +int asCScriptFunction::GetTypeId() const +{ + // This const cast is ok, the object won't be modified + asCDataType dt = asCDataType::CreateType(engine->FindMatchingFuncdef(const_cast(this), 0), false); + return engine->GetTypeIdFromDataType(dt); +} + +// interface +bool asCScriptFunction::IsCompatibleWithTypeId(int typeId) const +{ + asCDataType dt = engine->GetDataTypeFromTypeId(typeId); + + // Make sure the type is a function + if (!dt.IsFuncdef()) + return false; + + asCScriptFunction *func = CastToFuncdefType(dt.GetTypeInfo())->funcdef; + if( !IsSignatureExceptNameEqual(func) ) + return false; + + // If this is a class method, then only return true if the object type is the same + if( objectType != func->objectType ) + return false; + + return true; +} + +// interface +const char *asCScriptFunction::GetModuleName() const +{ + if( module ) + { + return module->name.AddressOf(); + } + + return 0; +} + +// interface +asIScriptModule *asCScriptFunction::GetModule() const +{ + return module; +} + +// interface +asITypeInfo *asCScriptFunction::GetObjectType() const +{ + return objectType; +} + +// interface +const char *asCScriptFunction::GetObjectName() const +{ + if( objectType ) + return objectType->GetName(); + + return 0; +} + +// interface +const char *asCScriptFunction::GetName() const +{ + return name.AddressOf(); +} + +// interface +const char *asCScriptFunction::GetNamespace() const +{ + if (nameSpace) + return nameSpace->name.AddressOf(); + + return 0; +} + +// interface +bool asCScriptFunction::IsReadOnly() const +{ + return isReadOnly; +} + +// interface +bool asCScriptFunction::IsPrivate() const +{ + return isPrivate; +} + +// interface +bool asCScriptFunction::IsProtected() const +{ + return isProtected; +} + +// internal +int asCScriptFunction::GetSpaceNeededForArguments() +{ + // We need to check the size for each type + int s = 0; + for( asUINT n = 0; n < parameterTypes.GetLength(); n++ ) + s += parameterTypes[n].GetSizeOnStackDWords(); + + return s; +} + +// internal +int asCScriptFunction::GetSpaceNeededForReturnValue() +{ + return returnType.GetSizeOnStackDWords(); +} + +// internal +bool asCScriptFunction::DoesReturnOnStack() const +{ + if( returnType.GetTypeInfo() && + (returnType.GetTypeInfo()->flags & asOBJ_VALUE) && + !returnType.IsReference() ) + return true; + + return false; +} + +// internal +asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool includeNamespace, bool includeParamNames) const +{ + asCString str; + + // TODO: default arg: Make the declaration with the default args an option + + // Don't add the return type for constructors and destructors + if( !(returnType.GetTokenType() == ttVoid && + objectType && + (name == objectType->name || (name.GetLength() > 0 && name[0] == '~') || + name == "$beh0" || name == "$beh2")) ) + { + str = returnType.Format(nameSpace, includeNamespace); + str += " "; + } + if( objectType && includeObjectName ) + { + if( includeNamespace && objectType->nameSpace->name != "" ) + str += objectType->nameSpace->name + "::"; + + if( objectType->name != "" ) + str += objectType->name + "::"; + else + str += "_unnamed_type_::"; + } + else if (funcdefType && funcdefType->parentClass && includeObjectName) + { + if (includeNamespace && funcdefType->parentClass->nameSpace->name != "") + str += funcdefType->parentClass->nameSpace->name + "::"; + + if (funcdefType->parentClass->name != "") + str += funcdefType->parentClass->name + "::"; + else + str += "_unnamed_type_::"; + } + else if( includeNamespace && nameSpace->name != "" ) + { + str += nameSpace->name + "::"; + } + if( name == "" ) + str += "_unnamed_function_("; + else if( name.SubString(0,4) == "$beh" && name.GetLength() == 5 ) + { + if( name[4] == '0' + asBEHAVE_CONSTRUCT ) + str += objectType->name + "("; + else if( name[4] == '0' + asBEHAVE_FACTORY ) + str += returnType.GetTypeInfo()->name + "("; + else if( name[4] == '0' + asBEHAVE_DESTRUCT ) + str += "~" + objectType->name + "("; + else + str += name + "("; + } + else + str += name + "("; + + if( parameterTypes.GetLength() > 0 ) + { + asUINT n; + for( n = 0; n < parameterTypes.GetLength() - 1; n++ ) + { + str += parameterTypes[n].Format(nameSpace, includeNamespace); + if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) + { + if( inOutFlags[n] == asTM_INREF ) str += "in"; + else if( inOutFlags[n] == asTM_OUTREF ) str += "out"; + else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; + } + + if( includeParamNames && n < parameterNames.GetLength() && parameterNames[n].GetLength() != 0 ) + { + str += " "; + str += parameterNames[n]; + } + + if( defaultArgs.GetLength() > n && defaultArgs[n] ) + { + asCString tmp; + tmp.Format(" = %s", defaultArgs[n]->AddressOf()); + str += tmp; + } + + str += ", "; + } + + // Add the last parameter + str += parameterTypes[n].Format(nameSpace, includeNamespace); + if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) + { + if( inOutFlags[n] == asTM_INREF ) str += "in"; + else if( inOutFlags[n] == asTM_OUTREF ) str += "out"; + else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; + } + + if( includeParamNames && n < parameterNames.GetLength() && parameterNames[n].GetLength() != 0 ) + { + str += " "; + str += parameterNames[n]; + } + + if( defaultArgs.GetLength() > n && defaultArgs[n] ) + { + asCString tmp; + tmp.Format(" = %s", defaultArgs[n]->AddressOf()); + str += tmp; + } + } + + str += ")"; + + if( isReadOnly ) + str += " const"; + + // Add the declaration of the list pattern + if( listPattern ) + { + asSListPatternNode *n = listPattern; + bool first = true; + while( n ) + { + if( n->type == asLPT_START ) + { + str += " {"; + first = true; + } + else if( n->type == asLPT_END ) + { + str += " }"; + first = false; + } + else if( n->type == asLPT_REPEAT ) + str += " repeat"; + else if( n->type == asLPT_REPEAT_SAME ) + str += " repeat_same"; + else if( n->type == asLPT_TYPE ) + { + if( first ) + { + str += " "; + first = false; + } + else + str += ", "; + str += reinterpret_cast(n)->dataType.Format(nameSpace, includeNamespace); + } + + n = n->next; + } + } + + return str; +} + +// interface +int asCScriptFunction::FindNextLineWithCode(int line) const +{ + if( scriptData == 0 ) return -1; + if( scriptData->lineNumbers.GetLength() == 0 ) return -1; + + // The line numbers for constructors are not in order due to the way + // class members can be initialized directly in the declaration + if( objectType && objectType->name == name ) + { + // Sort all line numbers before looking for the next + asCArray lineNbrs; + for( asUINT n = 1; n < scriptData->lineNumbers.GetLength(); n += 2 ) + lineNbrs.PushLast(scriptData->lineNumbers[n]&0xFFFFF); + + struct C + { + static int cmp(const void *a, const void *b) { return *(int*)a - *(int*)b; } + }; + std::qsort(&lineNbrs[0], lineNbrs.GetLength(), sizeof(int), C::cmp); + + if( line < lineNbrs[0] && line < (scriptData->declaredAt&0xFFFFF)) return -1; + if( line > lineNbrs[lineNbrs.GetLength()-1] ) return -1; + + // Find the line with code on or right after the input line + // TODO: optimize: Do binary search + for( asUINT n = 0; n < lineNbrs.GetLength(); n++ ) + if( line <= lineNbrs[n] ) + return lineNbrs[n]; + } + else + { + // Check if given line is outside function + if( line < (scriptData->declaredAt&0xFFFFF) ) return -1; + if( line > (scriptData->lineNumbers[scriptData->lineNumbers.GetLength()-1]&0xFFFFF) ) return -1; + + // Find the line with code on or right after the input line + // TODO: optimize: Do binary search instead + for( asUINT n = 1; n < scriptData->lineNumbers.GetLength(); n += 2 ) + { + if( line <= (scriptData->lineNumbers[n]&0xFFFFF) ) + return (scriptData->lineNumbers[n]&0xFFFFF); + } + } + + return -1; +} + +// internal +int asCScriptFunction::GetLineNumber(int programPosition, int *sectionIdx) +{ + asASSERT( scriptData ); + + if( sectionIdx ) *sectionIdx = scriptData->scriptSectionIdx; + if( scriptData->lineNumbers.GetLength() == 0 ) return 0; + + if( sectionIdx ) + { + // Find the correct section index if the function is compiled from multiple sections + // This array will be empty most of the time so we don't need a sofisticated algorithm to search it + for( asUINT n = 0; n < scriptData->sectionIdxs.GetLength(); n += 2 ) + { + if( scriptData->sectionIdxs[n] <= programPosition ) + *sectionIdx = scriptData->sectionIdxs[n+1]; + } + } + + // Do a binary search in the buffer + int max = (int)scriptData->lineNumbers.GetLength()/2 - 1; + int min = 0; + int i = max/2; + + for(;;) + { + if( scriptData->lineNumbers[i*2] < programPosition ) + { + // Have we found the largest number < programPosition? + if( max == i ) return scriptData->lineNumbers[i*2+1]; + if( scriptData->lineNumbers[i*2+2] > programPosition ) return scriptData->lineNumbers[i*2+1]; + + min = i + 1; + i = (max + min)/2; + } + else if( scriptData->lineNumbers[i*2] > programPosition ) + { + // Have we found the smallest number > programPosition? + if( min == i ) return scriptData->lineNumbers[i*2+1]; + + max = i - 1; + i = (max + min)/2; + } + else + { + // We found the exact position + return scriptData->lineNumbers[i*2+1]; + } + } +} + +// interface +asEFuncType asCScriptFunction::GetFuncType() const +{ + return funcType; +} + +// interface +asUINT asCScriptFunction::GetVarCount() const +{ + if( scriptData ) + return asUINT(scriptData->variables.GetLength()); + return 0; +} + +// interface +int asCScriptFunction::GetVar(asUINT index, const char **out_name, int *out_typeId) const +{ + if( scriptData == 0 ) + return asNOT_SUPPORTED; + if( index >= scriptData->variables.GetLength() ) + return asINVALID_ARG; + + if( out_name ) + *out_name = scriptData->variables[index]->name.AddressOf(); + if( out_typeId ) + *out_typeId = engine->GetTypeIdFromDataType(scriptData->variables[index]->type); + + return asSUCCESS; +} + +// interface +const char *asCScriptFunction::GetVarDecl(asUINT index, bool includeNamespace) const +{ + if( scriptData == 0 || index >= scriptData->variables.GetLength() ) + return 0; + + asCString *tempString = &asCThreadManager::GetLocalData()->string; + *tempString = scriptData->variables[index]->type.Format(nameSpace, includeNamespace); + *tempString += " " + scriptData->variables[index]->name; + + return tempString->AddressOf(); +} + +// internal +void asCScriptFunction::AddVariable(asCString &in_name, asCDataType &in_type, int in_stackOffset) +{ + asASSERT( scriptData ); + asSScriptVariable *var = asNEW(asSScriptVariable); + if( var == 0 ) + { + // Out of memory + return; + } + var->name = in_name; + var->type = in_type; + var->stackOffset = in_stackOffset; + var->declaredAtProgramPos = 0; + scriptData->variables.PushLast(var); +} + +// internal +asCTypeInfo *asCScriptFunction::GetTypeInfoOfLocalVar(short varOffset) +{ + asASSERT( scriptData ); + + for( asUINT n = 0; n < scriptData->objVariablePos.GetLength(); n++ ) + { + if( scriptData->objVariablePos[n] == varOffset ) + return scriptData->objVariableTypes[n]; + } + + return 0; +} + +// internal +void asCScriptFunction::ComputeSignatureId() +{ + // This function will compute the signatureId based on the + // function name, return type, and parameter types. The object + // type for methods is not used, so that class methods and + // interface methods match each other. + for( asUINT n = 0; n < engine->signatureIds.GetLength(); n++ ) + { + if( !IsSignatureEqual(engine->signatureIds[n]) ) continue; + + // We don't need to increment the reference counter here, because + // asCScriptEngine::FreeScriptFunctionId will maintain the signature + // id as the function is freed. + signatureId = engine->signatureIds[n]->signatureId; + return; + } + + signatureId = id; + engine->signatureIds.PushLast(this); +} + +// internal +bool asCScriptFunction::IsSignatureEqual(const asCScriptFunction *func) const +{ + if( name != func->name || !IsSignatureExceptNameEqual(func) ) return false; + + return true; +} + +// internal +bool asCScriptFunction::IsSignatureExceptNameEqual(const asCScriptFunction *func) const +{ + return IsSignatureExceptNameEqual(func->returnType, func->parameterTypes, func->inOutFlags, func->objectType, func->isReadOnly); +} + +// internal +bool asCScriptFunction::IsSignatureExceptNameEqual(const asCDataType &retType, const asCArray ¶mTypes, const asCArray ¶mInOut, const asCObjectType *objType, bool readOnly) const +{ + if( this->returnType != retType ) return false; + + return IsSignatureExceptNameAndReturnTypeEqual(paramTypes, paramInOut, objType, readOnly); +} + +// internal +bool asCScriptFunction::IsSignatureExceptNameAndObjectTypeEqual(const asCScriptFunction *func) const +{ + return IsSignatureExceptNameEqual(func->returnType, func->parameterTypes, func->inOutFlags, objectType, isReadOnly); +} + +// internal +bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCScriptFunction *func) const +{ + return IsSignatureExceptNameAndReturnTypeEqual(func->parameterTypes, func->inOutFlags, func->objectType, func->isReadOnly); +} + +// internal +bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCArray ¶mTypes, const asCArray ¶mInOut, const asCObjectType *objType, bool readOnly) const +{ + if( this->isReadOnly != readOnly ) return false; + if( (this->objectType != 0) != (objType != 0) ) return false; + if( this->inOutFlags != paramInOut ) return false; + if( this->parameterTypes != paramTypes ) return false; + + return true; +} + +// internal +void asCScriptFunction::AddReferences() +{ + // This array will be used to make sure we only add the reference to the same resource once + // This is especially important for global variables, as it expects the initialization function + // to hold only one reference to the variable. However, if the variable is initialized through + // the default constructor followed by the assignment operator we will have two references to + // the variable in the function. + asCArray ptrs; + + // Only count references if there is any bytecode + if( scriptData && scriptData->byteCode.GetLength() ) + { + if( returnType.GetTypeInfo() ) + { + returnType.GetTypeInfo()->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(returnType.GetTypeInfo()); + if( group != 0 ) group->AddRef(); + } + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].GetTypeInfo() ) + { + parameterTypes[p].GetTypeInfo()->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(parameterTypes[p].GetTypeInfo()); + if( group != 0 ) group->AddRef(); + } + + for( asUINT v = 0; v < scriptData->objVariableTypes.GetLength(); v++ ) + if( scriptData->objVariableTypes[v] ) // The null handle is also stored, but it doesn't have an object type + { + scriptData->objVariableTypes[v]->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(scriptData->objVariableTypes[v]); + if( group != 0 ) group->AddRef(); + } + + // Go through the byte code and add references to all resources used by the function + asCArray &bc = scriptData->byteCode; + for( asUINT n = 0; n < bc.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&bc[n]].type] ) + { + switch( *(asBYTE*)&bc[n] ) + { + // Object types + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + case asBC_RefCpyV: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); + asASSERT( objType ); + if( objType ) + objType->AddRefInternal(); + } + break; + + // Object type and function + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); + asASSERT( objType ); + if( objType ) + objType->AddRefInternal(); + + int funcId = asBC_INTARG(&bc[n]+AS_PTR_SIZE); + if( funcId ) + engine->scriptFunctions[funcId]->AddRefInternal(); + } + break; + + // Global variables + case asBC_PGA: + case asBC_PshGPtr: + case asBC_LDG: + case asBC_PshG4: + case asBC_LdGRdR4: + case asBC_CpyGtoV4: + case asBC_CpyVtoG4: + case asBC_SetG4: + // Need to increase the reference for each global variable + { + void *gvarPtr = (void*)asBC_PTRARG(&bc[n]); + if( !gvarPtr ) break; + asCGlobalProperty *prop = GetPropertyByGlobalVarPtr(gvarPtr); + if( !prop ) break; + + // Only addref the properties once + if( !ptrs.Exists(gvarPtr) ) + { + prop->AddRef(); + ptrs.PushLast(gvarPtr); + } + + asCConfigGroup *group = engine->FindConfigGroupForGlobalVar(prop->id); + if( group != 0 ) group->AddRef(); + } + break; + + // System functions + case asBC_CALLSYS: + { + int funcId = asBC_INTARG(&bc[n]); + asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId); + if( group != 0 ) group->AddRef(); + + asASSERT( funcId > 0 ); + if( funcId > 0 ) + engine->scriptFunctions[funcId]->AddRefInternal(); + } + break; + + // Functions + case asBC_CALL: + case asBC_CALLINTF: + { + int funcId = asBC_INTARG(&bc[n]); + asASSERT( funcId > 0 ); + if( funcId > 0 ) + engine->scriptFunctions[funcId]->AddRefInternal(); + } + break; + + // Function pointers + case asBC_FuncPtr: + { + asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); + asASSERT( func ); + if( func ) + func->AddRefInternal(); + } + break; + } + } + } +} + +// internal +void asCScriptFunction::ReleaseReferences() +{ + asCArray ptrs; + + // Only count references if there is any bytecode + if( scriptData && scriptData->byteCode.GetLength() ) + { + if( returnType.GetTypeInfo() ) + { + returnType.GetTypeInfo()->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(returnType.GetTypeInfo()); + if( group != 0 ) group->Release(); + } + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].GetTypeInfo() ) + { + parameterTypes[p].GetTypeInfo()->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(parameterTypes[p].GetTypeInfo()); + if( group != 0 ) group->Release(); + } + + for( asUINT v = 0; v < scriptData->objVariableTypes.GetLength(); v++ ) + if( scriptData->objVariableTypes[v] ) // The null handle is also stored, but it doesn't have an object type + { + scriptData->objVariableTypes[v]->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(scriptData->objVariableTypes[v]); + if( group != 0 ) group->Release(); + } + + // Go through the byte code and release references to all resources used by the function + asCArray &bc = scriptData->byteCode; + for( asUINT n = 0; n < bc.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&bc[n]].type] ) + { + switch( *(asBYTE*)&bc[n] ) + { + // Object types + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + case asBC_RefCpyV: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); + if( objType ) + objType->ReleaseInternal(); + } + break; + + // Object type and function + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); + if( objType ) + objType->ReleaseInternal(); + + int funcId = asBC_INTARG(&bc[n]+AS_PTR_SIZE); + if( funcId > 0 ) + { + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; + if( fptr ) + fptr->ReleaseInternal(); + + // The engine may have been forced to destroy the function internals early + // and this may will make it impossible to find the function by id anymore. + // This should only happen if the engine is released while the application + // is still keeping functions alive. + // TODO: Fix this possible memory leak + } + } + break; + + // Global variables + case asBC_PGA: + case asBC_PshGPtr: + case asBC_LDG: + case asBC_PshG4: + case asBC_LdGRdR4: + case asBC_CpyGtoV4: + case asBC_CpyVtoG4: + case asBC_SetG4: + // Need to increase the reference for each global variable + { + void *gvarPtr = (void*)asBC_PTRARG(&bc[n]); + if( !gvarPtr ) break; + asCGlobalProperty *prop = GetPropertyByGlobalVarPtr(gvarPtr); + if( !prop ) break; + + // Only release the properties once + if( !ptrs.Exists(gvarPtr) ) + { + prop->Release(); + ptrs.PushLast(gvarPtr); + } + + asCConfigGroup *group = engine->FindConfigGroupForGlobalVar(prop->id); + if( group != 0 ) group->Release(); + } + break; + + // System functions + case asBC_CALLSYS: + { + int funcId = asBC_INTARG(&bc[n]); + asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId); + if( group != 0 ) group->Release(); + + if( funcId ) + { + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; + if( fptr ) + fptr->ReleaseInternal(); + } + } + break; + + // Functions + case asBC_CALL: + case asBC_CALLINTF: + { + int funcId = asBC_INTARG(&bc[n]); + if( funcId ) + { + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; + if( fptr ) + fptr->ReleaseInternal(); + + // The engine may have been forced to destroy the function internals early + // and this may will make it impossible to find the function by id anymore. + // This should only happen if the engine is released while the application + // is still keeping functions alive. + // TODO: Fix this possible memory leak + } + } + break; + + // Function pointers + case asBC_FuncPtr: + { + asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); + if( func ) + func->ReleaseInternal(); + } + break; + } + } + + // Release the jit compiled function + if( scriptData->jitFunction ) + engine->jitCompiler->ReleaseJITFunction(scriptData->jitFunction); + scriptData->jitFunction = 0; + } + + // Delegate + if( objForDelegate ) + engine->ReleaseScriptObject(objForDelegate, funcForDelegate->GetObjectType()); + objForDelegate = 0; + if( funcForDelegate ) + funcForDelegate->Release(); + funcForDelegate = 0; +} + +// interface +int asCScriptFunction::GetReturnTypeId(asDWORD *flags) const +{ + if( flags ) + { + if( returnType.IsReference() ) + { + *flags = asTM_INOUTREF; + *flags |= returnType.IsReadOnly() ? asTM_CONST : 0; + } + else + *flags = asTM_NONE; + } + + return engine->GetTypeIdFromDataType(returnType); +} + +// interface +asUINT asCScriptFunction::GetParamCount() const +{ + return (asUINT)parameterTypes.GetLength(); +} + +// interface +int asCScriptFunction::GetParam(asUINT index, int *out_typeId, asDWORD *out_flags, const char **out_name, const char **out_defaultArg) const +{ + if( index >= parameterTypes.GetLength() ) + return asINVALID_ARG; + + if( out_typeId ) + *out_typeId = engine->GetTypeIdFromDataType(parameterTypes[index]); + + if( out_flags ) + { + *out_flags = inOutFlags[index]; + *out_flags |= parameterTypes[index].IsReadOnly() ? asTM_CONST : 0; + } + + if( out_name ) + { + // The parameter names are not stored if loading from bytecode without debug information + if( index < parameterNames.GetLength() ) + *out_name = parameterNames[index].AddressOf(); + else + *out_name = 0; + } + + if( out_defaultArg ) + { + if( index < defaultArgs.GetLength() && defaultArgs[index] ) + *out_defaultArg = defaultArgs[index]->AddressOf(); + else + *out_defaultArg = 0; + } + + return asSUCCESS; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2014-04-06, 2.29.0 +int asCScriptFunction::GetParamTypeId(asUINT index, asDWORD *flags) const +{ + if( index >= parameterTypes.GetLength() ) + return asINVALID_ARG; + + if( flags ) + { + *flags = inOutFlags[index]; + *flags |= parameterTypes[index].IsReadOnly() ? asTM_CONST : 0; + } + + return engine->GetTypeIdFromDataType(parameterTypes[index]); +} +#endif + +// interface +asIScriptEngine *asCScriptFunction::GetEngine() const +{ + return engine; +} + +// interface +const char *asCScriptFunction::GetDeclaration(bool includeObjectName, bool includeNamespace, bool includeParamNames) const +{ + asCString *tempString = &asCThreadManager::GetLocalData()->string; + *tempString = GetDeclarationStr(includeObjectName, includeNamespace, includeParamNames); + return tempString->AddressOf(); +} + +// interface +const char *asCScriptFunction::GetScriptSectionName() const +{ + if( scriptData && scriptData->scriptSectionIdx >= 0 ) + return engine->scriptSectionNames[scriptData->scriptSectionIdx]->AddressOf(); + + return 0; +} + +// interface +const char *asCScriptFunction::GetConfigGroup() const +{ + asCConfigGroup *group = 0; + if( funcType != asFUNC_FUNCDEF ) + group = engine->FindConfigGroupForFunction(id); + else + group = engine->FindConfigGroupForFuncDef(this->funcdefType); + + if( group == 0 ) + return 0; + + return group->groupName.AddressOf(); +} + +// interface +asDWORD asCScriptFunction::GetAccessMask() const +{ + return accessMask; +} + +// internal +void asCScriptFunction::JITCompile() +{ + if( funcType != asFUNC_SCRIPT ) + return; + + asASSERT( scriptData ); + + asIJITCompiler *jit = engine->GetJITCompiler(); + if( !jit ) + return; + + // Make sure the function has been compiled with JitEntry instructions + // For functions that has JitEntry this will be a quick test + asUINT length; + asDWORD *byteCode = GetByteCode(&length); + asDWORD *end = byteCode + length; + bool foundJitEntry = false; + while( byteCode < end ) + { + // Determine the instruction + asEBCInstr op = asEBCInstr(*(asBYTE*)byteCode); + if( op == asBC_JitEntry ) + { + foundJitEntry = true; + break; + } + + // Move to next instruction + byteCode += asBCTypeSize[asBCInfo[op].type]; + } + + if( !foundJitEntry ) + { + asCString msg; + msg.Format(TXT_NO_JIT_IN_FUNC_s, GetDeclaration()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + } + + // Release the previous function, if any + if( scriptData->jitFunction ) + { + engine->jitCompiler->ReleaseJITFunction(scriptData->jitFunction); + scriptData->jitFunction = 0; + } + + // Compile for native system + int r = jit->CompileFunction(this, &scriptData->jitFunction); + if( r < 0 ) + asASSERT( scriptData->jitFunction == 0 ); +} + +// interface +asDWORD *asCScriptFunction::GetByteCode(asUINT *length) +{ + if( scriptData == 0 ) return 0; + + if( length ) + *length = (asUINT)scriptData->byteCode.GetLength(); + + if( scriptData->byteCode.GetLength() ) + return scriptData->byteCode.AddressOf(); + + return 0; +} + +// interface +void *asCScriptFunction::SetUserData(void *data, asPWORD type) +{ + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *oldData = reinterpret_cast(userData[n+1]); + userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return 0; +} + +// interface +void *asCScriptFunction::GetUserData(asPWORD type) const +{ + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engine->engineRWLock); + + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + RELEASESHARED(engine->engineRWLock); + return reinterpret_cast(userData[n+1]); + } + } + + RELEASESHARED(engine->engineRWLock); + + return 0; +} + +// internal +// TODO: cleanup: This method should probably be a member of the engine +asCGlobalProperty *asCScriptFunction::GetPropertyByGlobalVarPtr(void *gvarPtr) +{ + asSMapNode *node; + if( engine->varAddressMap.MoveTo(&node, gvarPtr) ) + { + asASSERT(gvarPtr == node->value->GetAddressOfValue()); + return node->value; + } + return 0; +} + +// internal +int asCScriptFunction::GetRefCount() +{ + asASSERT( funcType == asFUNC_DELEGATE ); + + return externalRefCount.get(); +} + +// internal +void asCScriptFunction::SetFlag() +{ + gcFlag = true; +} + +// internal +bool asCScriptFunction::GetFlag() +{ + return gcFlag; +} + +// internal +void asCScriptFunction::EnumReferences(asIScriptEngine *) +{ + asASSERT( funcType == asFUNC_DELEGATE ); + + // Delegate + if( objForDelegate ) + engine->GCEnumCallback(objForDelegate); +} + +// internal +void asCScriptFunction::ReleaseAllHandles(asIScriptEngine *) +{ + asASSERT( funcType == asFUNC_DELEGATE ); + + // Release paramaters + + // Delegate + if( objForDelegate ) + engine->ReleaseScriptObject(objForDelegate, funcForDelegate->GetObjectType()); + objForDelegate = 0; +} + +// internal +bool asCScriptFunction::IsShared() const +{ + // All system functions are shared + if( funcType == asFUNC_SYSTEM ) return true; + + // All class methods for shared classes are also shared + asASSERT( objectType == 0 || objectType->engine == engine || objectType->engine == 0 ); + if( objectType && (objectType->flags & asOBJ_SHARED) ) return true; + + // Functions that have been specifically marked as shared are shared + return isShared; +} + +// internal +bool asCScriptFunction::IsFinal() const +{ + return isFinal; +} + +// internal +bool asCScriptFunction::IsOverride() const +{ + return isOverride; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_scriptnode.cpp b/3rdparty/angelscript/src/as_scriptnode.cpp new file mode 100644 index 0000000..9bd9cfd --- /dev/null +++ b/3rdparty/angelscript/src/as_scriptnode.cpp @@ -0,0 +1,178 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptnode.cpp +// +// A node in the script tree built by the parser for compilation +// + + + +#include "as_scriptnode.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCScriptNode::asCScriptNode(eScriptNode type) +{ + nodeType = type; + tokenType = ttUnrecognizedToken; + tokenPos = 0; + tokenLength = 0; + + parent = 0; + next = 0; + prev = 0; + firstChild = 0; + lastChild = 0; +} + +void asCScriptNode::Destroy(asCScriptEngine *engine) +{ + // Destroy all children + asCScriptNode *node = firstChild; + asCScriptNode *nxt; + + while( node ) + { + nxt = node->next; + node->Destroy(engine); + node = nxt; + } + + // Return the memory to the memory manager + engine->memoryMgr.FreeScriptNode(this); +} + +asCScriptNode *asCScriptNode::CreateCopy(asCScriptEngine *engine) +{ + void *ptr = engine->memoryMgr.AllocScriptNode(); + if( ptr == 0 ) + { + // Out of memory + return 0; + } + + new(ptr) asCScriptNode(nodeType); + + asCScriptNode *node = reinterpret_cast(ptr); + node->tokenLength = tokenLength; + node->tokenPos = tokenPos; + node->tokenType = tokenType; + + asCScriptNode *child = firstChild; + while( child ) + { + node->AddChildLast(child->CreateCopy(engine)); + child = child->next; + } + + return node; +} + +void asCScriptNode::SetToken(sToken *token) +{ + tokenType = token->type; +} + +void asCScriptNode::UpdateSourcePos(size_t pos, size_t length) +{ + if( pos == 0 && length == 0 ) return; + + if( tokenPos == 0 && tokenLength == 0 ) + { + tokenPos = pos; + tokenLength = length; + } + else + { + if( tokenPos > pos ) + { + tokenLength = tokenPos + tokenLength - pos; + tokenPos = pos; + } + + if( pos + length > tokenPos + tokenLength ) + { + tokenLength = pos + length - tokenPos; + } + } +} + +void asCScriptNode::AddChildLast(asCScriptNode *node) +{ + // We might get a null pointer if the parser encounter an out-of-memory situation + if( node == 0 ) return; + + if( lastChild ) + { + lastChild->next = node; + node->next = 0; + node->prev = lastChild; + node->parent = this; + lastChild = node; + } + else + { + firstChild = node; + lastChild = node; + node->next = 0; + node->prev = 0; + node->parent = this; + } + + UpdateSourcePos(node->tokenPos, node->tokenLength); +} + +void asCScriptNode::DisconnectParent() +{ + if( parent ) + { + if( parent->firstChild == this ) + parent->firstChild = next; + if( parent->lastChild == this ) + parent->lastChild = prev; + } + + if( next ) + next->prev = prev; + + if( prev ) + prev->next = next; + + parent = 0; + next = 0; + prev = 0; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_scriptobject.cpp b/3rdparty/angelscript/src/as_scriptobject.cpp new file mode 100644 index 0000000..21c8cc8 --- /dev/null +++ b/3rdparty/angelscript/src/as_scriptobject.cpp @@ -0,0 +1,1069 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#include +#include "as_config.h" +#include "as_scriptengine.h" +#include "as_scriptobject.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +// This helper function will call the default factory, that is a script function +asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine) +{ + asIScriptContext *ctx = 0; + int r = 0; + bool isNested = false; + + // Use nested call in the context if there is an active context + ctx = asGetActiveContext(); + if( ctx ) + { + // It may not always be possible to reuse the current context, + // in which case we'll have to create a new one any way. + if( ctx->GetEngine() == objType->GetEngine() && ctx->PushState() == asSUCCESS ) + isNested = true; + else + ctx = 0; + } + + if( ctx == 0 ) + { + // Request a context from the engine + ctx = engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? + return 0; + } + } + + r = ctx->Prepare(engine->scriptFunctions[objType->beh.factory]); + if( r < 0 ) + { + if( isNested ) + ctx->PopState(); + else + engine->ReturnContext(ctx); + return 0; + } + + for(;;) + { + r = ctx->Execute(); + + // We can't allow this execution to be suspended + // so resume the execution immediately + if( r != asEXECUTION_SUSPENDED ) + break; + } + + if( r != asEXECUTION_FINISHED ) + { + if( isNested ) + { + ctx->PopState(); + + // If the execution was aborted or an exception occurred, + // then we should forward that to the outer execution. + if( r == asEXECUTION_EXCEPTION ) + { + // TODO: How to improve this exception + ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL); + } + else if( r == asEXECUTION_ABORTED ) + ctx->Abort(); + } + else + engine->ReturnContext(ctx); + return 0; + } + + asIScriptObject *ptr = (asIScriptObject*)ctx->GetReturnAddress(); + + // Increase the reference, because the context will release its pointer + ptr->AddRef(); + + if( isNested ) + ctx->PopState(); + else + engine->ReturnContext(ctx); + + return ptr; +} + +#ifdef AS_MAX_PORTABILITY + +static void ScriptObject_AddRef_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptObject_Release_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->Release(); +} + +static void ScriptObject_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptObject_SetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptObject_GetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptObject_GetWeakRefFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + *(asILockableSharedBool**)gen->GetAddressOfReturnLocation() = self->GetWeakRefFlag(); +} + +static void ScriptObject_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +static void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + *self = *other; + + *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; +} + +static void ScriptObject_Construct_Generic(asIScriptGeneric *gen) +{ + asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + ScriptObject_Construct(objType, self); +} + +#endif + +void RegisterScriptObject(asCScriptEngine *engine) +{ + // Register the default script class behaviours + int r = 0; + UNUSED_VAR(r); // It is only used in debug mode + engine->scriptTypeBehaviours.engine = engine; + engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC; + engine->scriptTypeBehaviours.name = "$obj"; +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptObject,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + + // Weakref behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GET_WEAKREF_FLAG, "int &f()", asMETHOD(asCScriptObject,GetWeakRefFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptObject,GetRefCount), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptObject,SetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptObject,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptObject,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptObject,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); +#else + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptObject_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptObject_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + + // Weakref behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GET_WEAKREF_FLAG, "int &f()", asFUNCTION(ScriptObject_GetWeakRefFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptObject_GetRefCount_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptObject_SetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptObject_GetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptObject_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptObject_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); +#endif +} + +void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self) +{ + new(self) asCScriptObject(objType); +} + +void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self) +{ + new(self) asCScriptObject(objType, false); +} + +asCScriptObject::asCScriptObject(asCObjectType *ot, bool doInitialize) +{ + refCount.set(1); + objType = ot; + objType->AddRef(); + isDestructCalled = false; + extra = 0; + hasRefCountReachedZero = false; + + // Notify the garbage collector of this object + if( objType->flags & asOBJ_GC ) + objType->engine->gc.AddScriptObjectToGC(this, objType); + + // Initialize members to zero. Technically we only need to zero the pointer + // members, but just the memset is faster than having to loop and check the datatypes + memset((void*)(this+1), 0, objType->size - sizeof(asCScriptObject)); + + if( doInitialize ) + { +#ifdef AS_NO_MEMBER_INIT + // When member initialization is disabled the constructor must make sure + // to allocate and initialize all members with the default constructor + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() && !prop->type.IsObjectHandle() ) + { + if( prop->type.IsReference() || prop->type.GetTypeInfo()->flags & asOBJ_REF ) + { + asPWORD *ptr = reinterpret_cast(reinterpret_cast(this) + prop->byteOffset); + if( prop->type.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT ) + *ptr = (asPWORD)ScriptObjectFactory(prop->type.GetTypeInfo(), ot->engine); + else + *ptr = (asPWORD)AllocateUninitializedObject(prop->type.GetTypeInfo(), ot->engine); + } + } + } +#endif + } + else + { + // When the object is created without initialization, all non-handle members must be allocated, but not initialized + asCScriptEngine *engine = objType->engine; + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() && !prop->type.IsObjectHandle() ) + { + if( prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF) ) + { + asPWORD *ptr = reinterpret_cast(reinterpret_cast(this) + prop->byteOffset); + *ptr = (asPWORD)AllocateUninitializedObject(CastToObjectType(prop->type.GetTypeInfo()), engine); + } + } + } + } +} + +void asCScriptObject::Destruct() +{ + // Call the destructor, which will also call the GCObject's destructor + this->~asCScriptObject(); + + // Free the memory +#ifndef WIP_16BYTE_ALIGN + userFree(this); +#else + // Script object memory is allocated through asCScriptEngine::CallAlloc() + // This free call must match the allocator used in CallAlloc(). + userFreeAligned(this); +#endif +} + +asCScriptObject::~asCScriptObject() +{ + if( extra ) + { + if( extra->weakRefFlag ) + { + extra->weakRefFlag->Release(); + extra->weakRefFlag = 0; + } + + if( objType->engine ) + { + // Clean the user data + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n+1] ) + { + for( asUINT c = 0; c < objType->engine->cleanScriptObjectFuncs.GetLength(); c++ ) + if( objType->engine->cleanScriptObjectFuncs[c].type == extra->userData[n] ) + objType->engine->cleanScriptObjectFuncs[c].cleanFunc(this); + } + } + } + + asDELETE(extra, SExtra); + } + + // The engine pointer should be available from the objectType + asCScriptEngine *engine = objType->engine; + + // Destroy all properties + // In most cases the members are initialized in the order they have been declared, + // so it's safer to uninitialize them from last to first. The order may be different + // depending on the use of inheritance and or initialization in the declaration. + // TODO: Should the order of initialization be stored by the compiler so that the + // reverse order can be guaranteed during the destruction? + for( int n = (int)objType->properties.GetLength()-1; n >= 0; n-- ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + // Destroy the object + asCObjectType *propType = CastToObjectType(prop->type.GetTypeInfo()); + if( prop->type.IsReference() || propType->flags & asOBJ_REF ) + { + void **ptr = (void**)(((char*)this) + prop->byteOffset); + if( *ptr ) + { + FreeObject(*ptr, propType, engine); + *(asDWORD*)ptr = 0; + } + } + else + { + // The object is allocated inline. As only POD objects may be allocated inline + // it is not a problem to call the destructor even if the object may never have + // been initialized, e.g. if an exception interrupted the constructor. + asASSERT( propType->flags & asOBJ_POD ); + + void *ptr = (void**)(((char*)this) + prop->byteOffset); + if( propType->beh.destruct ) + engine->CallObjectMethod(ptr, propType->beh.destruct); + } + } + else if( prop->type.IsFuncdef() ) + { + // Release the function descriptor + asCScriptFunction **ptr = (asCScriptFunction**)(((char*)this) + prop->byteOffset); + if (*ptr) + { + (*ptr)->Release(); + *ptr = 0; + } + } + } + + objType->Release(); + objType = 0; + + // Something is really wrong if the refCount is not 0 by now + asASSERT( refCount.get() == 0 ); +} + +asILockableSharedBool *asCScriptObject::GetWeakRefFlag() const +{ + // If the object's refCount has already reached zero then the object is already + // about to be destroyed so it's ok to return null if the weakRefFlag doesn't already + // exist + if( (extra && extra->weakRefFlag) || hasRefCountReachedZero ) + return extra->weakRefFlag; + + // Lock globally so no other thread can attempt + // to create a shared bool at the same time. + // TODO: runtime optimize: Instead of locking globally, it would be possible to have + // a critical section per object type. This would reduce the + // chances of two threads lock on the same critical section. + asAcquireExclusiveLock(); + + // Make sure another thread didn't create the + // flag while we waited for the lock + if( !extra ) + extra = asNEW(SExtra); + if( !extra->weakRefFlag ) + extra->weakRefFlag = asNEW(asCLockableSharedBool); + + asReleaseExclusiveLock(); + + return extra->weakRefFlag; +} + +void *asCScriptObject::GetUserData(asPWORD type) const +{ + if( !extra ) + return 0; + + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + // TODO: runtime optimize: Would it be worth it to have a rwlock per object type? + asAcquireSharedLock(); + + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n] == type ) + { + void *userData = reinterpret_cast(extra->userData[n+1]); + asReleaseSharedLock(); + return userData; + } + } + + asReleaseSharedLock(); + + return 0; +} + +void *asCScriptObject::SetUserData(void *data, asPWORD type) +{ + // Lock globally so no other thread can attempt + // to manipulate the extra data at the same time. + // TODO: runtime optimize: Instead of locking globally, it would be possible to have + // a critical section per object type. This would reduce the + // chances of two threads lock on the same critical section. + asAcquireExclusiveLock(); + + // Make sure another thread didn't create the + // flag while we waited for the lock + if( !extra ) + extra = asNEW(SExtra); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n] == type ) + { + void *oldData = reinterpret_cast(extra->userData[n+1]); + extra->userData[n+1] = reinterpret_cast(data); + + asReleaseExclusiveLock(); + + return oldData; + } + } + + extra->userData.PushLast(type); + extra->userData.PushLast(reinterpret_cast(data)); + + asReleaseExclusiveLock(); + + return 0; +} + +asIScriptEngine *asCScriptObject::GetEngine() const +{ + return objType->engine; +} + +int asCScriptObject::AddRef() const +{ + // Warn in case the application tries to increase the refCount after it has reached zero. + // This may happen for example if the application calls a method on the class while it is + // being destroyed. The application shouldn't do this because it may cause application + // crashes if members that have already been destroyed are accessed accidentally. + if( hasRefCountReachedZero ) + { + if( objType && objType->engine ) + { + asCString msg; + msg.Format(TXT_RESURRECTING_SCRIPTOBJECT_s, objType->name.AddressOf()); + objType->engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); + } + } + + // Increase counter and clear flag set by GC + gcFlag = false; + return refCount.atomicInc(); +} + +int asCScriptObject::Release() const +{ + // Clear the flag set by the GC + gcFlag = false; + + // If the weak ref flag exists it is because someone held a weak ref + // and that someone may add a reference to the object at any time. It + // is ok to check the existance of the weakRefFlag without locking here + // because if the refCount is 1 then no other thread is currently + // creating the weakRefFlag. + if( refCount.get() == 1 && extra && extra->weakRefFlag ) + { + // Set the flag to tell others that the object is no longer alive + // We must do this before decreasing the refCount to 0 so we don't + // end up with a race condition between this thread attempting to + // destroy the object and the other that temporary added a strong + // ref from the weak ref. + extra->weakRefFlag->Set(true); + } + + // Call the script destructor behaviour if the reference counter is 1. + if( refCount.get() == 1 && !isDestructCalled ) + { + // This cast is OK since we are the last reference + const_cast(this)->CallDestructor(); + } + + // Now do the actual releasing + int r = refCount.atomicDec(); + if( r == 0 ) + { + // Flag this object as being destroyed so the application + // can be warned if the code attempts to resurrect the object + // during the destructor. This also avoids a recursive call + // to the destructor which would crash the application if it + // really does resurrect the object. + if( !hasRefCountReachedZero ) + { + hasRefCountReachedZero = true; + + // This cast is OK since we are the last reference + const_cast(this)->Destruct(); + } + return 0; + } + + return r; +} + +void asCScriptObject::CallDestructor() +{ + // Only allow the destructor to be called once + if( isDestructCalled ) return; + + asIScriptContext *ctx = 0; + bool isNested = false; + bool doAbort = false; + + // Make sure the destructor is called once only, even if the + // reference count is increased and then decreased again + isDestructCalled = true; + + // Call the destructor for this class and all the super classes + asCObjectType *ot = objType; + while( ot ) + { + int funcIndex = ot->beh.destruct; + if( funcIndex ) + { + if( ctx == 0 ) + { + // Check for active context first as it is quicker + // to reuse than to set up a new one. + ctx = asGetActiveContext(); + if( ctx ) + { + if( ctx->GetEngine() == objType->GetEngine() && ctx->PushState() == asSUCCESS ) + isNested = true; + else + ctx = 0; + } + + if( ctx == 0 ) + { + // Request a context from the engine + ctx = objType->engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? + return; + } + } + } + + int r = ctx->Prepare(objType->engine->scriptFunctions[funcIndex]); + if( r >= 0 ) + { + ctx->SetObject(this); + + for(;;) + { + r = ctx->Execute(); + + // If the script tries to suspend itself just restart it + if( r != asEXECUTION_SUSPENDED ) + break; + } + + // Exceptions in the destructor will be ignored, as there is not much + // that can be done about them. However a request to abort the execution + // will be forwarded to the outer execution, in case of a nested call. + if( r == asEXECUTION_ABORTED ) + doAbort = true; + + // Observe, even though the current destructor was aborted or an exception + // occurred, we still try to execute the base class' destructor if available + // in order to free as many resources as possible. + } + } + + ot = ot->derivedFrom; + } + + if( ctx ) + { + if( isNested ) + { + ctx->PopState(); + + // Forward any request to abort the execution to the outer call + if( doAbort ) + ctx->Abort(); + } + else + { + // Return the context to engine + objType->engine->ReturnContext(ctx); + } + } +} + +asITypeInfo *asCScriptObject::GetObjectType() const +{ + return objType; +} + +int asCScriptObject::GetRefCount() +{ + return refCount.get(); +} + +void asCScriptObject::SetFlag() +{ + gcFlag = true; +} + +bool asCScriptObject::GetFlag() +{ + return gcFlag; +} + +// interface +int asCScriptObject::GetTypeId() const +{ + asCDataType dt = asCDataType::CreateType(objType, false); + return objType->engine->GetTypeIdFromDataType(dt); +} + +asUINT asCScriptObject::GetPropertyCount() const +{ + return asUINT(objType->properties.GetLength()); +} + +int asCScriptObject::GetPropertyTypeId(asUINT prop) const +{ + if( prop >= objType->properties.GetLength() ) + return asINVALID_ARG; + + return objType->engine->GetTypeIdFromDataType(objType->properties[prop]->type); +} + +const char *asCScriptObject::GetPropertyName(asUINT prop) const +{ + if( prop >= objType->properties.GetLength() ) + return 0; + + return objType->properties[prop]->name.AddressOf(); +} + +void *asCScriptObject::GetAddressOfProperty(asUINT prop) +{ + if( prop >= objType->properties.GetLength() ) + return 0; + + // Objects are stored by reference, so this must be dereferenced + asCDataType *dt = &objType->properties[prop]->type; + if( dt->IsObject() && !dt->IsObjectHandle() && + (dt->IsReference() || dt->GetTypeInfo()->flags & asOBJ_REF) ) + return *(void**)(((char*)this) + objType->properties[prop]->byteOffset); + + return (void*)(((char*)this) + objType->properties[prop]->byteOffset); +} + +void asCScriptObject::EnumReferences(asIScriptEngine *engine) +{ + // We'll notify the GC of all object handles that we're holding + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + void *ptr = 0; + if( prop->type.IsObject() ) + { + // TODO: gc: The members of the value type needs to be enumerated + // too, since the value type may be holding a reference. + if( prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF) ) + ptr = *(void**)(((char*)this) + prop->byteOffset); + else + ptr = (void*)(((char*)this) + prop->byteOffset); + } + else if (prop->type.IsFuncdef()) + ptr = *(void**)(((char*)this) + prop->byteOffset); + + if (ptr) + ((asCScriptEngine*)engine)->GCEnumCallback(ptr); + } +} + +void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine) +{ + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + + // TODO: gc: The members of the members needs to be released + // too, since they may be holding a reference. Even + // if the member is a value type. + if( prop->type.IsObject() && prop->type.IsObjectHandle() ) + { + void **ptr = (void**)(((char*)this) + prop->byteOffset); + if( *ptr ) + { + asASSERT( (prop->type.GetTypeInfo()->flags & asOBJ_NOCOUNT) || prop->type.GetBehaviour()->release ); + if( prop->type.GetBehaviour()->release ) + ((asCScriptEngine*)engine)->CallObjectMethod(*ptr, prop->type.GetBehaviour()->release); + *ptr = 0; + } + } + else if (prop->type.IsFuncdef()) + { + asCScriptFunction **ptr = (asCScriptFunction**)(((char*)this) + prop->byteOffset); + if (*ptr) + { + (*ptr)->Release(); + *ptr = 0; + } + } + } +} + +asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self) +{ + return (*self = *other); +} + +asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other) +{ + if( &other != this ) + { + if( !other.objType->DerivesFrom(objType) ) + { + // We cannot allow a value assignment from a type that isn't the same or + // derives from this type as the member properties may not have the same layout + asIScriptContext *ctx = asGetActiveContext(); + ctx->SetException(TXT_MISMATCH_IN_VALUE_ASSIGN); + return *this; + } + + // If the script class implements the opAssign method, it should be called + asCScriptEngine *engine = objType->engine; + asCScriptFunction *func = engine->scriptFunctions[objType->beh.copy]; + if( func->funcType == asFUNC_SYSTEM ) + { + // Copy all properties + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + void **dst = (void**)(((char*)this) + prop->byteOffset); + void **src = (void**)(((char*)&other) + prop->byteOffset); + if( !prop->type.IsObjectHandle() ) + { + if( prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF) ) + CopyObject(*src, *dst, CastToObjectType(prop->type.GetTypeInfo()), engine); + else + CopyObject(src, dst, CastToObjectType(prop->type.GetTypeInfo()), engine); + } + else + CopyHandle((asPWORD*)src, (asPWORD*)dst, CastToObjectType(prop->type.GetTypeInfo()), engine); + } + else if (prop->type.IsFuncdef()) + { + asCScriptFunction **dst = (asCScriptFunction**)(((char*)this) + prop->byteOffset); + asCScriptFunction **src = (asCScriptFunction**)(((char*)&other) + prop->byteOffset); + if (*dst) + (*dst)->Release(); + *dst = *src; + if (*dst) + (*dst)->AddRef(); + } + else + { + void *dst = ((char*)this) + prop->byteOffset; + void *src = ((char*)&other) + prop->byteOffset; + memcpy(dst, src, prop->type.GetSizeInMemoryBytes()); + } + } + } + else + { + // Reuse the active context or create a new one to call the script class' opAssign method + asIScriptContext *ctx = 0; + int r = 0; + bool isNested = false; + + ctx = asGetActiveContext(); + if( ctx ) + { + if( ctx->GetEngine() == engine && ctx->PushState() == asSUCCESS ) + isNested = true; + else + ctx = 0; + } + + if( ctx == 0 ) + { + // Request a context from the engine + ctx = engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? + return *this; + } + } + + r = ctx->Prepare(engine->scriptFunctions[objType->beh.copy]); + if( r < 0 ) + { + if( isNested ) + ctx->PopState(); + else + engine->ReturnContext(ctx); + // TODO: How to best report this failure? + return *this; + } + + r = ctx->SetArgAddress(0, const_cast(&other)); + asASSERT( r >= 0 ); + r = ctx->SetObject(this); + asASSERT( r >= 0 ); + + for(;;) + { + r = ctx->Execute(); + + // We can't allow this execution to be suspended + // so resume the execution immediately + if( r != asEXECUTION_SUSPENDED ) + break; + } + + if( r != asEXECUTION_FINISHED ) + { + if( isNested ) + { + ctx->PopState(); + + // If the execution was aborted or an exception occurred, + // then we should forward that to the outer execution. + if( r == asEXECUTION_EXCEPTION ) + { + // TODO: How to improve this exception + ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL); + } + else if( r == asEXECUTION_ABORTED ) + ctx->Abort(); + } + else + { + // Return the context to the engine + engine->ReturnContext(ctx); + } + return *this; + } + + if( isNested ) + ctx->PopState(); + else + { + // Return the context to the engine + engine->ReturnContext(ctx); + } + } + } + + return *this; +} + +int asCScriptObject::CopyFrom(asIScriptObject *other) +{ + if( other == 0 ) return asINVALID_ARG; + + if( GetTypeId() != other->GetTypeId() ) + return asINVALID_TYPE; + + *this = *(asCScriptObject*)other; + + return 0; +} + +void *asCScriptObject::AllocateUninitializedObject(asCObjectType *in_objType, asCScriptEngine *engine) +{ + void *ptr = 0; + + if( in_objType->flags & asOBJ_SCRIPT_OBJECT ) + { + ptr = engine->CallAlloc(in_objType); + ScriptObject_ConstructUnitialized(in_objType, reinterpret_cast(ptr)); + } + else if( in_objType->flags & asOBJ_TEMPLATE ) + { + // Templates store the original factory that takes the object + // type as a hidden parameter in the construct behaviour + ptr = engine->CallGlobalFunctionRetPtr(in_objType->beh.construct, in_objType); + } + else if( in_objType->flags & asOBJ_REF ) + { + ptr = engine->CallGlobalFunctionRetPtr(in_objType->beh.factory); + } + else + { + ptr = engine->CallAlloc(in_objType); + int funcIndex = in_objType->beh.construct; + if( funcIndex ) + engine->CallObjectMethod(ptr, funcIndex); + } + + return ptr; +} + +void asCScriptObject::FreeObject(void *ptr, asCObjectType *in_objType, asCScriptEngine *engine) +{ + if( in_objType->flags & asOBJ_REF ) + { + asASSERT( (in_objType->flags & asOBJ_NOCOUNT) || in_objType->beh.release ); + if(in_objType->beh.release ) + engine->CallObjectMethod(ptr, in_objType->beh.release); + } + else + { + if( in_objType->beh.destruct ) + engine->CallObjectMethod(ptr, in_objType->beh.destruct); + + engine->CallFree(ptr); + } +} + +void asCScriptObject::CopyObject(void *src, void *dst, asCObjectType *in_objType, asCScriptEngine *engine) +{ + int funcIndex = in_objType->beh.copy; + if( funcIndex ) + { + asCScriptFunction *func = engine->scriptFunctions[in_objType->beh.copy]; + if( func->funcType == asFUNC_SYSTEM ) + engine->CallObjectMethod(dst, src, funcIndex); + else + { + // Call the script class' opAssign method + asASSERT(in_objType->flags & asOBJ_SCRIPT_OBJECT ); + reinterpret_cast(dst)->CopyFrom(reinterpret_cast(src)); + } + } + else if( in_objType->size && (in_objType->flags & asOBJ_POD) ) + memcpy(dst, src, in_objType->size); +} + +void asCScriptObject::CopyHandle(asPWORD *src, asPWORD *dst, asCObjectType *in_objType, asCScriptEngine *engine) +{ + // asOBJ_NOCOUNT doesn't have addref or release behaviours + asASSERT( (in_objType->flags & asOBJ_NOCOUNT) || (in_objType->beh.release && in_objType->beh.addref) ); + + if( *dst && in_objType->beh.release ) + engine->CallObjectMethod(*(void**)dst, in_objType->beh.release); + *dst = *src; + if( *dst && in_objType->beh.addref ) + engine->CallObjectMethod(*(void**)dst, in_objType->beh.addref); +} + +// TODO: weak: Should move to its own file +asCLockableSharedBool::asCLockableSharedBool() : value(false) +{ + refCount.set(1); +} + +int asCLockableSharedBool::AddRef() const +{ + return refCount.atomicInc(); +} + +int asCLockableSharedBool::Release() const +{ + int r = refCount.atomicDec(); + if( r == 0 ) + asDELETE(const_cast(this), asCLockableSharedBool); + return r; +} + +bool asCLockableSharedBool::Get() const +{ + return value; +} + +void asCLockableSharedBool::Set(bool v) +{ + // Make sure the value is not changed while another thread + // is inspecting it and taking a decision on what to do. + Lock(); + value = v; + Unlock(); +} + +void asCLockableSharedBool::Lock() const +{ + ENTERCRITICALSECTION(lock); +} + +void asCLockableSharedBool::Unlock() const +{ + LEAVECRITICALSECTION(lock); +} + +// Interface +// Auxiliary function to allow applications to create shared +// booleans without having to implement the logic for them +AS_API asILockableSharedBool *asCreateLockableSharedBool() +{ + return asNEW(asCLockableSharedBool); +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_string.cpp b/3rdparty/angelscript/src/as_string.cpp new file mode 100644 index 0000000..1bfddfa --- /dev/null +++ b/3rdparty/angelscript/src/as_string.cpp @@ -0,0 +1,483 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +#include "as_config.h" + +#include // va_list, va_start(), etc +#include // strtod(), strtol() +#include // some compilers declare memcpy() here + +#if !defined(AS_NO_MEMORY_H) +#include +#endif + +#include "as_string.h" +#include "as_string_util.h" + +asCString::asCString() +{ + length = 0; + local[0] = 0; +} + +// Copy constructor +asCString::asCString(const asCString &str) +{ + length = 0; + local[0] = 0; + + Assign(str.AddressOf(), str.length); +} + +#ifdef AS_CAN_USE_CPP11 +asCString::asCString(asCString &&str) +{ + if( str.length <= 11 ) + { + length = str.length; + memcpy(local, str.local, length); + local[length] = 0; + } + else + { + dynamic = str.dynamic; + length = str.length; + } + + str.dynamic = 0; + str.length = 0; +} +#endif // c++11 + +asCString::asCString(const char *str, size_t len) +{ + length = 0; + local[0] = 0; + + Assign(str, len); +} + +asCString::asCString(const char *str) +{ + length = 0; + local[0] = 0; + + size_t len = strlen(str); + Assign(str, len); +} + +asCString::asCString(char ch) +{ + length = 0; + local[0] = 0; + + Assign(&ch, 1); +} + +asCString::~asCString() +{ + if( length > 11 && dynamic ) + { + asDELETEARRAY(dynamic); + } +} + +char *asCString::AddressOf() +{ + if( length <= 11 ) + return local; + else + return dynamic; +} + +const char *asCString::AddressOf() const +{ + if( length <= 11 ) + return local; + else + return dynamic; +} + +void asCString::SetLength(size_t len) +{ + Allocate(len, true); +} + +void asCString::Allocate(size_t len, bool keepData) +{ + // If we stored the capacity of the dynamically allocated buffer it would be possible + // to save some memory allocations if a string decreases in size then increases again, + // but this would require extra bytes in the string object itself, or a decrease of + // the static buffer, which in turn would mean extra memory is needed. I've tested each + // of these options, and it turned out that the current choice is what best balanced + // the number of allocations against the size of the allocations. + + if( len > 11 && len > length ) + { + // Allocate a new dynamic buffer if the new one is larger than the old + char *buf = asNEWARRAY(char,len+1); + if( buf == 0 ) + { + // Out of memory. Return without modifying anything + return; + } + + if( keepData ) + { + int l = (int)len < (int)length ? (int)len : (int)length; + memcpy(buf, AddressOf(), l); + } + + if( length > 11 ) + { + asDELETEARRAY(dynamic); + } + + dynamic = buf; + } + else if( len <= 11 && length > 11 ) + { + // Free the dynamic buffer, since it is no longer needed + char *buf = dynamic; + if( keepData ) + { + memcpy(&local, buf, len); + } + asDELETEARRAY(buf); + } + + length = (int)len; + + // Make sure the buffer is null terminated + AddressOf()[length] = 0; +} + +void asCString::Assign(const char *str, size_t len) +{ + Allocate(len, false); + + // Copy the string + memcpy(AddressOf(), str, length); + AddressOf()[length] = 0; +} + +asCString &asCString::operator =(const char *str) +{ + size_t len = str ? strlen(str) : 0; + Assign(str, len); + + return *this; +} + +asCString &asCString::operator =(const asCString &str) +{ + Assign(str.AddressOf(), str.length); + + return *this; +} + +#ifdef AS_CAN_USE_CPP11 +asCString &asCString::operator =(asCString &&str) +{ + if( this != &str ) + { + if( length > 11 && dynamic ) + { + asDELETEARRAY(dynamic); + } + + if ( str.length <= 11 ) + { + length = str.length; + + memcpy(local, str.local, length); + local[length] = 0; + } + else + { + dynamic = str.dynamic; + length = str.length; + } + + str.dynamic = 0; + str.length = 0; + } + + return *this; +} +#endif // c++11 + +asCString &asCString::operator =(char ch) +{ + Assign(&ch, 1); + + return *this; +} + +void asCString::Concatenate(const char *str, size_t len) +{ + asUINT oldLength = length; + SetLength(length + len); + + memcpy(AddressOf() + oldLength, str, len); + AddressOf()[length] = 0; +} + +asCString &asCString::operator +=(const char *str) +{ + size_t len = strlen(str); + Concatenate(str, len); + + return *this; +} + +asCString &asCString::operator +=(const asCString &str) +{ + Concatenate(str.AddressOf(), str.length); + + return *this; +} + +asCString &asCString::operator +=(char ch) +{ + Concatenate(&ch, 1); + + return *this; +} + +size_t asCString::GetLength() const +{ + return length; +} + +// Returns the length +size_t asCString::Format(const char *format, ...) +{ + va_list args; + va_start(args, format); + + char tmp[256]; + int r = asVSNPRINTF(tmp, 255, format, args); + + if( r > 0 ) + { + Assign(tmp, r); + } + else + { + size_t n = 512; + asCString str; // Use temporary string in case the current buffer is a parameter + str.Allocate(n, false); + + while( (r = asVSNPRINTF(str.AddressOf(), n, format, args)) < 0 ) + { + n *= 2; + str.Allocate(n, false); + } + + Assign(str.AddressOf(), r); + } + + va_end(args); + + return length; +} + +char &asCString::operator [](size_t index) +{ + asASSERT(index < length); + + return AddressOf()[index]; +} + +const char &asCString::operator [](size_t index) const +{ + asASSERT(index < length); + + return AddressOf()[index]; +} + +asCString asCString::SubString(size_t in_start, size_t in_length) const +{ + if( in_start >= GetLength() || in_length == 0 ) + return asCString(""); + + if( in_length == (size_t)(-1) ) in_length = GetLength() - in_start; + + asCString tmp; + tmp.Assign(AddressOf() + in_start, in_length); + + return tmp; +} + +int asCString::Compare(const char *str) const +{ + return asCompareStrings(AddressOf(), length, str, strlen(str)); +} + +int asCString::Compare(const asCString &str) const +{ + return asCompareStrings(AddressOf(), length, str.AddressOf(), str.GetLength()); +} + +int asCString::Compare(const char *str, size_t len) const +{ + return asCompareStrings(AddressOf(), length, str, len); +} + +size_t asCString::RecalculateLength() +{ + SetLength(strlen(AddressOf())); + + return length; +} + +int asCString::FindLast(const char *str, int *count) const +{ + // There is no strstr that starts from the end, so + // we'll iterate until we find the last occurrance. + // This shouldn't cause a performance problem because + // it is not expected that this will be done very often, + // and then only on quite short strings anyway. + + if( count ) *count = 0; + + const char *last = 0; + const char *curr = AddressOf()-1; + while( (curr = strstr(curr+1, str)) != 0 ) + { + if( count ) (*count)++; + last = curr; + } + + if( last ) + return int(last - AddressOf()); + + return -1; +} + +//----------------------------------------------------------------------------- +// Helper functions + +bool operator ==(const asCString &a, const char *b) +{ + return a.Compare(b) == 0; +} + +bool operator !=(const asCString &a, const char *b) +{ + return a.Compare(b) != 0; +} + +bool operator ==(const asCString &a, const asCString &b) +{ + return a.Compare(b) == 0; +} + +bool operator !=(const asCString &a, const asCString &b) +{ + return a.Compare(b) != 0; +} + +bool operator ==(const char *a, const asCString &b) +{ + return b.Compare(a) == 0; +} + +bool operator !=(const char *a, const asCString &b) +{ + return b.Compare(a) != 0; +} + +bool operator <(const asCString &a, const asCString &b) +{ + return a.Compare(b) < 0; +} + +asCString operator +(const asCString &a, const asCString &b) +{ + asCString res = a; + res += b; + + return res; +} + +asCString operator +(const char *a, const asCString &b) +{ + asCString res = a; + res += b; + + return res; +} + +asCString operator +(const asCString &a, const char *b) +{ + asCString res = a; + res += b; + + return res; +} + +// wrapper class + +asCStringPointer::asCStringPointer() + : string(0), length(0), cstring(0) +{ +} + +asCStringPointer::asCStringPointer(const char *str, size_t len) + : string(str), length(len), cstring(0) +{ +} + +asCStringPointer::asCStringPointer(asCString *cstr) + : string(0), length(0), cstring(cstr) +{ +} + +const char *asCStringPointer::AddressOf() const +{ + return string ? string : cstring->AddressOf(); +} + +size_t asCStringPointer::GetLength() const +{ + return string ? length : cstring->GetLength(); +} + +bool asCStringPointer::operator==(const asCStringPointer& other) const +{ + return asCompareStrings(AddressOf(), GetLength(), other.AddressOf(), other.GetLength()) == 0; +} + +bool asCStringPointer::operator<(const asCStringPointer& other) const +{ + return asCompareStrings(AddressOf(), GetLength(), other.AddressOf(), other.GetLength()) < 0; +} diff --git a/3rdparty/angelscript/src/as_string_util.cpp b/3rdparty/angelscript/src/as_string_util.cpp new file mode 100644 index 0000000..1596b42 --- /dev/null +++ b/3rdparty/angelscript/src/as_string_util.cpp @@ -0,0 +1,382 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com + +*/ + +#include "as_config.h" + +#include // some compilers declare memcpy() here +#include // pow() +#include // UINT64_MAX + +#if !defined(AS_NO_MEMORY_H) +#include +#endif + +#include "as_string.h" +#include "as_string_util.h" + +BEGIN_AS_NAMESPACE + +int asCompareStrings(const char *str1, size_t len1, const char *str2, size_t len2) +{ + if( len1 == 0 ) + { + if( str2 == 0 || len2 == 0 ) return 0; // Equal + + return 1; // The other string is larger than this + } + + if( str2 == 0 ) + { + if( len1 == 0 ) + return 0; // Equal + + return -1; // The other string is smaller than this + } + + if( len2 < len1 ) + { + int result = memcmp(str1, str2, len2); + if( result == 0 ) return -1; // The other string is smaller than this + + return result; + } + + int result = memcmp(str1, str2, len1); + if( result == 0 && len1 < len2 ) return 1; // The other string is larger than this + + return result; +} + +double asStringScanDouble(const char *string, size_t *numScanned) +{ + // I decided to do my own implementation of strtod() because this function + // doesn't seem to be present on all systems. iOS 5 for example doesn't appear + // to include the function in the standard lib. + + // Another reason is that the standard implementation of strtod() is dependent + // on the locale on some systems, i.e. it may use comma instead of dot for + // the decimal indicator. This can be avoided by forcing the locale to "C" with + // setlocale(), but this is another thing that is highly platform dependent. + + double value = 0; + double fraction = 0.1; + int exponent = 0; + bool negativeExponent = false; + int c = 0; + + // The tokenizer separates the sign from the number in + // two tokens so we'll never have a sign to parse here + + // Parse the integer value + for( ;; ) + { + if( string[c] >= '0' && string[c] <= '9' ) + value = value*10 + double(string[c] - '0'); + else + break; + + c++; + } + + if( string[c] == '.' ) + { + c++; + + // Parse the fraction + for( ;; ) + { + if( string[c] >= '0' && string[c] <= '9' ) + value += fraction * double(string[c] - '0'); + else + break; + + c++; + fraction *= 0.1; + } + } + + if( string[c] == 'e' || string[c] == 'E' ) + { + c++; + + // Parse the sign of the exponent + if( string[c] == '-' ) + { + negativeExponent = true; + c++; + } + else if( string[c] == '+' ) + c++; + + // Parse the exponent value + for( ;; ) + { + if( string[c] >= '0' && string[c] <= '9' ) + exponent = exponent*10 + int(string[c] - '0'); + else + break; + + c++; + } + } + + if( exponent ) + { + if( negativeExponent ) + exponent = -exponent; + value *= pow(10.0, exponent); + } + + if( numScanned ) + *numScanned = c; + + return value; +} + +// Converts a character to the decimal number based on the radix +// Returns -1 if the character is not valid for the radix +static int asCharToNbr(char ch, int radix) +{ + if( ch >= '0' && ch <= '9' ) return ((ch -= '0') < radix ? ch : -1); + if( ch >= 'A' && ch <= 'Z' ) return ((ch -= 'A'-10) < radix ? ch : -1); + if( ch >= 'a' && ch <= 'z' ) return ((ch -= 'a'-10) < radix ? ch : -1); + return -1; +} + +// If base is 0 the string should be prefixed by 0x, 0d, 0o, or 0b to allow the function to automatically determine the radix +asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned, bool *overflow) +{ + asASSERT(base == 10 || base == 16 || base == 0); + + if (overflow) + *overflow = false; + + const char *end = string; + + asQWORD res = 0; + if( base == 10 ) + { + while( *end >= '0' && *end <= '9' ) + { + if( overflow && ((res > UINT64_MAX / 10) || ((asUINT(*end - '0') > (UINT64_MAX - (UINT64_MAX / 10) * 10)) && res == UINT64_MAX / 10)) ) + *overflow = true; + res *= 10; + res += *end++ - '0'; + } + } + else + { + if( base == 0 && string[0] == '0') + { + // Determine the radix from the prefix + switch( string[1] ) + { + case 'b': case 'B': base = 2; break; + case 'o': case 'O': base = 8; break; + case 'd': case 'D': base = 10; break; + case 'x': case 'X': base = 16; break; + } + end += 2; + } + + asASSERT( base ); + + if( base ) + { + for (int nbr; (nbr = asCharToNbr(*end, base)) >= 0; end++) + { + if (overflow && ((res > UINT64_MAX / base) || ((asUINT(nbr) > (UINT64_MAX - (UINT64_MAX / base) * base)) && res == UINT64_MAX / base)) ) + *overflow = true; + + res = res * base + nbr; + } + } + } + + if( numScanned ) + *numScanned = end - string; + + return res; +} + +// +// The function will encode the unicode code point into the outEncodedBuffer, and then +// return the length of the encoded value. If the input value is not a valid unicode code +// point, then the function will return -1. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer) +{ + unsigned char *buf = (unsigned char*)outEncodedBuffer; + + int length = -1; + + if( value <= 0x7F ) + { + buf[0] = static_cast(value); + return 1; + } + else if( value >= 0x80 && value <= 0x7FF ) + { + // Encode it with 2 characters + buf[0] = static_cast(0xC0 + (value >> 6)); + length = 2; + } + else if( (value >= 0x800 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFF) ) + { + // Note: Values 0xD800 to 0xDFFF are not valid unicode characters + buf[0] = static_cast(0xE0 + (value >> 12)); + length = 3; + } + else if( value >= 0x10000 && value <= 0x10FFFF ) + { + buf[0] = static_cast(0xF0 + (value >> 18)); + length = 4; + } + + int n = length-1; + for( ; n > 0; n-- ) + { + buf[n] = static_cast(0x80 + (value & 0x3F)); + value >>= 6; + } + + return length; +} + +// +// The function will decode an UTF8 character and return the unicode code point. +// outLength will receive the number of bytes that were decoded. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength) +{ + const unsigned char *buf = (const unsigned char*)encodedBuffer; + + int value = 0; + int length = -1; + unsigned char byte = buf[0]; + if( (byte & 0x80) == 0 ) + { + // This is the only byte + if( outLength ) *outLength = 1; + return byte; + } + else if( (byte & 0xE0) == 0xC0 ) + { + // There is one more byte + value = int(byte & 0x1F); + length = 2; + + // The value at this moment must not be less than 2, because + // that should have been encoded with one byte only. + if( value < 2 ) + length = -1; + } + else if( (byte & 0xF0) == 0xE0 ) + { + // There are two more bytes + value = int(byte & 0x0F); + length = 3; + } + else if( (byte & 0xF8) == 0xF0 ) + { + // There are three more bytes + value = int(byte & 0x07); + length = 4; + } + + int n = 1; + for( ; n < length; n++ ) + { + byte = buf[n]; + if( (byte & 0xC0) == 0x80 ) + value = (value << 6) + int(byte & 0x3F); + else + break; + } + + if( n == length ) + { + if( outLength ) *outLength = (unsigned)length; + return value; + } + + // The byte sequence isn't a valid UTF-8 byte sequence. + return -1; +} + +// +// The function will encode the unicode code point into the outEncodedBuffer, and then +// return the length of the encoded value. If the input value is not a valid unicode code +// point, then the function will return -1. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer) +{ + if( value < 0x10000 ) + { +#ifndef AS_BIG_ENDIAN + outEncodedBuffer[0] = (value & 0xFF); + outEncodedBuffer[1] = ((value >> 8) & 0xFF); +#else + outEncodedBuffer[1] = (value & 0xFF); + outEncodedBuffer[0] = ((value >> 8) & 0xFF); +#endif + return 2; + } + else + { + value -= 0x10000; + int surrogate1 = ((value >> 10) & 0x3FF) + 0xD800; + int surrogate2 = (value & 0x3FF) + 0xDC00; + +#ifndef AS_BIG_ENDIAN + outEncodedBuffer[0] = (surrogate1 & 0xFF); + outEncodedBuffer[1] = ((surrogate1 >> 8) & 0xFF); + outEncodedBuffer[2] = (surrogate2 & 0xFF); + outEncodedBuffer[3] = ((surrogate2 >> 8) & 0xFF); +#else + outEncodedBuffer[1] = (surrogate1 & 0xFF); + outEncodedBuffer[0] = ((surrogate1 >> 8) & 0xFF); + outEncodedBuffer[3] = (surrogate2 & 0xFF); + outEncodedBuffer[2] = ((surrogate2 >> 8) & 0xFF); +#endif + + return 4; + } +} + + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/as_thread.cpp b/3rdparty/angelscript/src/as_thread.cpp new file mode 100644 index 0000000..63ab15e --- /dev/null +++ b/3rdparty/angelscript/src/as_thread.cpp @@ -0,0 +1,468 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2014 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_thread.cpp +// +// Functions for multi threading support +// + +#include "as_config.h" +#include "as_thread.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +//======================================================================= + +// Singleton +static asCThreadManager *threadManager = 0; + +//====================================================================== + +// Global API functions +extern "C" +{ + +AS_API int asThreadCleanup() +{ + return asCThreadManager::CleanupLocalData(); +} + +AS_API asIThreadManager *asGetThreadManager() +{ + return threadManager; +} + +AS_API int asPrepareMultithread(asIThreadManager *externalThreadMgr) +{ + return asCThreadManager::Prepare(externalThreadMgr); +} + +AS_API void asUnprepareMultithread() +{ + asCThreadManager::Unprepare(); +} + +AS_API void asAcquireExclusiveLock() +{ + if( threadManager ) + { + ACQUIREEXCLUSIVE(threadManager->appRWLock); + } +} + +AS_API void asReleaseExclusiveLock() +{ + if( threadManager ) + { + RELEASEEXCLUSIVE(threadManager->appRWLock); + } +} + +AS_API void asAcquireSharedLock() +{ + if( threadManager ) + { + ACQUIRESHARED(threadManager->appRWLock); + } +} + +AS_API void asReleaseSharedLock() +{ + if( threadManager ) + { + RELEASESHARED(threadManager->appRWLock); + } +} + +} + +//====================================================================== + +#if !defined(AS_NO_THREADS) && defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) +__declspec(thread) asCThreadLocalData *asCThreadManager::tld = 0; +#endif + +asCThreadManager::asCThreadManager() +{ + // We're already in the critical section when this function is called + +#ifdef AS_NO_THREADS + tld = 0; +#else + // Allocate the thread local storage + #if defined AS_POSIX_THREADS + pthread_key_t pKey; + pthread_key_create(&pKey, 0); + tlsKey = (asDWORD)pKey; + #elif defined AS_WINDOWS_THREADS + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + tld = 0; + #else + tlsKey = (asDWORD)TlsAlloc(); + #endif + #endif +#endif + refCount = 1; +} + +int asCThreadManager::Prepare(asIThreadManager *externalThreadMgr) +{ + // Don't allow an external thread manager if there + // is already a thread manager defined + if( externalThreadMgr && threadManager ) + return asINVALID_ARG; + + // The critical section cannot be declared globally, as there is no + // guarantee for the order in which global variables are initialized + // or uninitialized. + + // For this reason it's not possible to prevent two threads from calling + // AddRef at the same time, so there is a chance for a race condition here. + + // To avoid the race condition when the thread manager is first created, + // the application must make sure to call the global asPrepareForMultiThread() + // in the main thread before any other thread creates a script engine. + if( threadManager == 0 && externalThreadMgr == 0 ) + threadManager = asNEW(asCThreadManager); + else + { + // If an application uses different dlls each dll will get it's own memory + // space for global variables. If multiple dlls then uses AngelScript's + // global thread support functions it is then best to share the thread + // manager to make sure all dlls use the same critical section. + if( externalThreadMgr ) + threadManager = reinterpret_cast(externalThreadMgr); + + ENTERCRITICALSECTION(threadManager->criticalSection); + threadManager->refCount++; + LEAVECRITICALSECTION(threadManager->criticalSection); + } + + // Success + return 0; +} + +void asCThreadManager::Unprepare() +{ + asASSERT(threadManager); + + if( threadManager == 0 ) + return; + + // It's necessary to protect this section so no + // other thread attempts to call AddRef or Release + // while clean up is in progress. + ENTERCRITICALSECTION(threadManager->criticalSection); + if( --threadManager->refCount == 0 ) + { + // Make sure the local data is destroyed, at least for the current thread + CleanupLocalData(); + + // As the critical section will be destroyed together + // with the thread manager we must first clear the global + // variable in case a new thread manager needs to be created; + asCThreadManager *mgr = threadManager; + threadManager = 0; + + // Leave the critical section before it is destroyed + LEAVECRITICALSECTION(mgr->criticalSection); + + asDELETE(mgr,asCThreadManager); + } + else + LEAVECRITICALSECTION(threadManager->criticalSection); +} + +asCThreadManager::~asCThreadManager() +{ +#ifndef AS_NO_THREADS + // Deallocate the thread local storage + #if defined AS_POSIX_THREADS + pthread_key_delete((pthread_key_t)tlsKey); + #elif defined AS_WINDOWS_THREADS + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + tld = 0; + #else + TlsFree((DWORD)tlsKey); + #endif + #endif +#else + if( tld ) + { + asDELETE(tld,asCThreadLocalData); + } + tld = 0; +#endif +} + +int asCThreadManager::CleanupLocalData() +{ + if( threadManager == 0 ) + return 0; + +#ifndef AS_NO_THREADS +#if defined AS_POSIX_THREADS + asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey); +#elif defined AS_WINDOWS_THREADS + #if !defined(_MSC_VER) || !(WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey); + #endif +#endif + + if( tld == 0 ) + return 0; + + if( tld->activeContexts.GetLength() == 0 ) + { + asDELETE(tld,asCThreadLocalData); + #if defined AS_POSIX_THREADS + pthread_setspecific((pthread_key_t)threadManager->tlsKey, 0); + #elif defined AS_WINDOWS_THREADS + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + tld = 0; + #else + TlsSetValue((DWORD)threadManager->tlsKey, 0); + #endif + #endif + return 0; + } + else + return asCONTEXT_ACTIVE; + +#else + if( threadManager->tld ) + { + if( threadManager->tld->activeContexts.GetLength() == 0 ) + { + asDELETE(threadManager->tld,asCThreadLocalData); + threadManager->tld = 0; + } + else + return asCONTEXT_ACTIVE; + } + return 0; +#endif +} + +asCThreadLocalData *asCThreadManager::GetLocalData() +{ + if( threadManager == 0 ) + return 0; + +#ifndef AS_NO_THREADS +#if defined AS_POSIX_THREADS + asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey); + if( tld == 0 ) + { + tld = asNEW(asCThreadLocalData)(); + pthread_setspecific((pthread_key_t)threadManager->tlsKey, tld); + } +#elif defined AS_WINDOWS_THREADS + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + if( tld == 0 ) + tld = asNEW(asCThreadLocalData)(); + #else + asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey); + if( tld == 0 ) + { + tld = asNEW(asCThreadLocalData)(); + TlsSetValue((DWORD)threadManager->tlsKey, tld); + } + #endif +#endif + + return tld; +#else + if( threadManager->tld == 0 ) + threadManager->tld = asNEW(asCThreadLocalData)(); + + return threadManager->tld; +#endif +} + +//========================================================================= + +asCThreadLocalData::asCThreadLocalData() +{ +} + +asCThreadLocalData::~asCThreadLocalData() +{ +} + +//========================================================================= + +#ifndef AS_NO_THREADS +asCThreadCriticalSection::asCThreadCriticalSection() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_init(&cs, 0); +#elif defined AS_WINDOWS_THREADS +#if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + // Only the Ex version is available on Windows Store + InitializeCriticalSectionEx(&cs, 4000, 0); +#else + // Only the non-Ex version is available on WinXP and older + // MinGW also only defines this version + InitializeCriticalSection(&cs); +#endif +#endif +} + +asCThreadCriticalSection::~asCThreadCriticalSection() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_destroy(&cs); +#elif defined AS_WINDOWS_THREADS + DeleteCriticalSection(&cs); +#endif +} + +void asCThreadCriticalSection::Enter() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_lock(&cs); +#elif defined AS_WINDOWS_THREADS + EnterCriticalSection(&cs); +#endif +} + +void asCThreadCriticalSection::Leave() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_unlock(&cs); +#elif defined AS_WINDOWS_THREADS + LeaveCriticalSection(&cs); +#endif +} + +bool asCThreadCriticalSection::TryEnter() +{ +#if defined AS_POSIX_THREADS + return !pthread_mutex_trylock(&cs); +#elif defined AS_WINDOWS_THREADS + return TryEnterCriticalSection(&cs) ? true : false; +#else + return true; +#endif +} + +asCThreadReadWriteLock::asCThreadReadWriteLock() +{ +#if defined AS_POSIX_THREADS + int r = pthread_rwlock_init(&lock, 0); + asASSERT( r == 0 ); + UNUSED_VAR(r); +#elif defined AS_WINDOWS_THREADS +#if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + // Only the Ex versions are available on Windows Store + + // Create a semaphore to allow up to maxReaders simultaneous readers + readLocks = CreateSemaphoreExW(NULL, maxReaders, maxReaders, 0, 0, 0); + // Create a critical section to synchronize writers + InitializeCriticalSectionEx(&writeLock, 4000, 0); +#else + readLocks = CreateSemaphoreW(NULL, maxReaders, maxReaders, 0); + InitializeCriticalSection(&writeLock); +#endif +#endif +} + +asCThreadReadWriteLock::~asCThreadReadWriteLock() +{ +#if defined AS_POSIX_THREADS + pthread_rwlock_destroy(&lock); +#elif defined AS_WINDOWS_THREADS + DeleteCriticalSection(&writeLock); + CloseHandle(readLocks); +#endif +} + +void asCThreadReadWriteLock::AcquireExclusive() +{ +#if defined AS_POSIX_THREADS + pthread_rwlock_wrlock(&lock); +#elif defined AS_WINDOWS_THREADS + // Synchronize writers, so only one tries to lock out the readers + EnterCriticalSection(&writeLock); + + // Lock all reader out from the semaphore. Do this one by one, + // so the lock doesn't have to wait until there are no readers at all. + // If we try to lock all at once it is quite possible the writer will + // never succeed. + for( asUINT n = 0; n < maxReaders; n++ ) + WaitForSingleObjectEx(readLocks, INFINITE, FALSE); + + // Allow another writer to lock. It will only be able to + // lock the readers when this writer releases them anyway. + LeaveCriticalSection(&writeLock); +#endif +} + +void asCThreadReadWriteLock::ReleaseExclusive() +{ +#if defined AS_POSIX_THREADS + pthread_rwlock_unlock(&lock); +#elif defined AS_WINDOWS_THREADS + // Release all readers at once + ReleaseSemaphore(readLocks, maxReaders, 0); +#endif +} + +void asCThreadReadWriteLock::AcquireShared() +{ +#if defined AS_POSIX_THREADS + pthread_rwlock_rdlock(&lock); +#elif defined AS_WINDOWS_THREADS + // Lock a reader slot + WaitForSingleObjectEx(readLocks, INFINITE, FALSE); +#endif +} + +void asCThreadReadWriteLock::ReleaseShared() +{ +#if defined AS_POSIX_THREADS + pthread_rwlock_unlock(&lock); +#elif defined AS_WINDOWS_THREADS + // Release the reader slot + ReleaseSemaphore(readLocks, 1, 0); +#endif +} + +#endif + +//======================================================================== + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_tokenizer.cpp b/3rdparty/angelscript/src/as_tokenizer.cpp new file mode 100644 index 0000000..2017a12 --- /dev/null +++ b/3rdparty/angelscript/src/as_tokenizer.cpp @@ -0,0 +1,475 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2015 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokenizer.cpp +// +// This class identifies tokens from the script code +// + +#include "as_config.h" +#include "as_scriptengine.h" +#include "as_tokenizer.h" +#include "as_tokendef.h" + +#if !defined(AS_NO_MEMORY_H) +#include +#endif +#include // strcmp() + +BEGIN_AS_NAMESPACE + +asCTokenizer::asCTokenizer() +{ + engine = 0; + memset(keywordTable, 0, sizeof(keywordTable)); + + // Initialize the jump table + for( asUINT n = 0; n < numTokenWords; n++ ) + { + const sTokenWord& current = tokenWords[n]; + unsigned char start = current.word[0]; + + // Create new jump table entry if none exists + if( !keywordTable[start] ) + { + // Surely there won't ever be more than 32 keywords starting with + // the same character. Right? + keywordTable[start] = asNEWARRAY(const sTokenWord*, 32); + memset(keywordTable[start], 0, sizeof(sTokenWord*)*32); + } + + // Add the token sorted from longest to shortest so + // we check keywords greedily. + const sTokenWord** tok = keywordTable[start]; + unsigned insert = 0, index = 0; + while( tok[index] ) + { + if(tok[index]->wordLength >= current.wordLength) + ++insert; + ++index; + } + + while( index > insert ) + { + tok[index] = tok[index - 1]; + --index; + } + + tok[insert] = ¤t; + } +} + +asCTokenizer::~asCTokenizer() +{ + // Deallocate the jump table + for( asUINT n = 0; n < 256; n++ ) + { + if( keywordTable[n] ) + asDELETEARRAY(keywordTable[n]); + } +} + +// static +const char *asCTokenizer::GetDefinition(int tokenType) +{ + if( tokenType == ttUnrecognizedToken ) return ""; + if( tokenType == ttEnd ) return ""; + if( tokenType == ttWhiteSpace ) return ""; + if( tokenType == ttOnelineComment ) return ""; + if( tokenType == ttMultilineComment ) return ""; + if( tokenType == ttIdentifier ) return ""; + if( tokenType == ttIntConstant ) return ""; + if( tokenType == ttFloatConstant ) return ""; + if( tokenType == ttDoubleConstant ) return ""; + if( tokenType == ttStringConstant ) return ""; + if( tokenType == ttMultilineStringConstant ) return ""; + if( tokenType == ttNonTerminatedStringConstant ) return ""; + if( tokenType == ttBitsConstant ) return ""; + if( tokenType == ttHeredocStringConstant ) return ""; + + for( asUINT n = 0; n < numTokenWords; n++ ) + if( tokenWords[n].tokenType == tokenType ) + return tokenWords[n].word; + + return 0; +} + +bool asCTokenizer::IsDigitInRadix(char ch, int radix) const +{ + if( ch >= '0' && ch <= '9' ) return (ch -= '0') < radix; + if( ch >= 'A' && ch <= 'Z' ) return (ch -= 'A'-10) < radix; + if( ch >= 'a' && ch <= 'z' ) return (ch -= 'a'-10) < radix; + return false; +} + +eTokenType asCTokenizer::GetToken(const char *source, size_t sourceLength, size_t *tokenLength, asETokenClass *tc) const +{ + asASSERT(source != 0); + asASSERT(tokenLength != 0); + + eTokenType tokenType; + size_t tlen; + asETokenClass t = ParseToken(source, sourceLength, tlen, tokenType); + if( tc ) *tc = t; + if( tokenLength ) *tokenLength = tlen; + + return tokenType; +} + +asETokenClass asCTokenizer::ParseToken(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + if( IsWhiteSpace(source, sourceLength, tokenLength, tokenType) ) return asTC_WHITESPACE; + if( IsComment(source, sourceLength, tokenLength, tokenType) ) return asTC_COMMENT; + if( IsConstant(source, sourceLength, tokenLength, tokenType) ) return asTC_VALUE; + if( IsIdentifier(source, sourceLength, tokenLength, tokenType) ) return asTC_IDENTIFIER; + if( IsKeyWord(source, sourceLength, tokenLength, tokenType) ) return asTC_KEYWORD; + + // If none of the above this is an unrecognized token + // We can find the length of the token by advancing + // one step and trying to identify a token there + tokenType = ttUnrecognizedToken; + tokenLength = 1; + + return asTC_UNKNOWN; +} + +bool asCTokenizer::IsWhiteSpace(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + // Treat UTF8 byte-order-mark (EF BB BF) as whitespace + if( sourceLength >= 3 && + asBYTE(source[0]) == 0xEFu && + asBYTE(source[1]) == 0xBBu && + asBYTE(source[2]) == 0xBFu ) + { + tokenType = ttWhiteSpace; + tokenLength = 3; + return true; + } + + // Group all other white space characters into one + size_t n; + int numWsChars = (int)strlen(whiteSpace); + for( n = 0; n < sourceLength; n++ ) + { + bool isWhiteSpace = false; + for( int w = 0; w < numWsChars; w++ ) + { + if( source[n] == whiteSpace[w] ) + { + isWhiteSpace = true; + break; + } + } + if( !isWhiteSpace ) break; + } + + if( n > 0 ) + { + tokenType = ttWhiteSpace; + tokenLength = n; + return true; + } + + return false; +} + +bool asCTokenizer::IsComment(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + if( sourceLength < 2 ) + return false; + + if( source[0] != '/' ) + return false; + + if( source[1] == '/' ) + { + // One-line comment + + // Find the length + size_t n; + for( n = 2; n < sourceLength; n++ ) + { + if( source[n] == '\n' ) + break; + } + + tokenType = ttOnelineComment; + tokenLength = n < sourceLength ? n+1 : n; + + return true; + } + + if( source[1] == '*' ) + { + // Multi-line comment + + // Find the length + size_t n; + for( n = 2; n < sourceLength-1; ) + { + if( source[n++] == '*' && source[n] == '/' ) + break; + } + + tokenType = ttMultilineComment; + tokenLength = n+1; + + return true; + } + + return false; +} + +bool asCTokenizer::IsConstant(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + // Starting with number + if( (source[0] >= '0' && source[0] <= '9') || (source[0] == '.' && sourceLength > 1 && source[1] >= '0' && source[1] <= '9') ) + { + // Is it a based number? + if( source[0] == '0' && sourceLength > 1 ) + { + // Determine the radix for the constant + int radix = 0; + switch( source[1] ) + { + case 'b': case 'B': radix = 2; break; + case 'o': case 'O': radix = 8; break; + case 'd': case 'D': radix = 10; break; + case 'x': case 'X': radix = 16; break; + } + + if( radix ) + { + size_t n; + for( n = 2; n < sourceLength; n++ ) + if( !IsDigitInRadix(source[n], radix) ) + break; + + tokenType = ttBitsConstant; + tokenLength = n; + return true; + } + } + + size_t n; + for( n = 0; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + + if( n < sourceLength && (source[n] == '.' || source[n] == 'e' || source[n] == 'E') ) + { + if( source[n] == '.' ) + { + n++; + for( ; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + } + + if( n < sourceLength && (source[n] == 'e' || source[n] == 'E') ) + { + n++; + if( n < sourceLength && (source[n] == '-' || source[n] == '+') ) + n++; + + for( ; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + } + + if( n < sourceLength && (source[n] == 'f' || source[n] == 'F') ) + { + tokenType = ttFloatConstant; + tokenLength = n + 1; + } + else + { +#ifdef AS_USE_DOUBLE_AS_FLOAT + tokenType = ttFloatConstant; +#else + tokenType = ttDoubleConstant; +#endif + tokenLength = n; + } + return true; + } + + tokenType = ttIntConstant; + tokenLength = n; + return true; + } + + // String constant between double or single quotes + if( source[0] == '"' || source[0] == '\'' ) + { + // Is it a normal string constant or a heredoc string constant? + if( sourceLength >= 6 && source[0] == '"' && source[1] == '"' && source[2] == '"' ) + { + // Heredoc string constant (spans multiple lines, no escape sequences) + + // Find the length + size_t n; + for( n = 3; n < sourceLength-2; n++ ) + { + if( source[n] == '"' && source[n+1] == '"' && source[n+2] == '"' ) + break; + } + + tokenType = ttHeredocStringConstant; + tokenLength = n+3; + } + else + { + // Normal string constant + tokenType = ttStringConstant; + char quote = source[0]; + bool evenSlashes = true; + size_t n; + for( n = 1; n < sourceLength; n++ ) + { +#ifdef AS_DOUBLEBYTE_CHARSET + // Double-byte characters are only allowed for ASCII + if( (source[n] & 0x80) && engine->ep.scanner == 0 ) + { + // This is a leading character in a double byte character, + // include both in the string and continue processing. + n++; + continue; + } +#endif + + if( source[n] == '\n' ) + tokenType = ttMultilineStringConstant; + if( source[n] == quote && evenSlashes ) + { + tokenLength = n+1; + return true; + } + if( source[n] == '\\' ) evenSlashes = !evenSlashes; else evenSlashes = true; + } + + tokenType = ttNonTerminatedStringConstant; + tokenLength = n; + } + + return true; + } + + return false; +} + +bool asCTokenizer::IsIdentifier(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + // char is unsigned by default on some architectures, e.g. ppc and arm + // Make sure the value is always treated as signed in the below comparisons + signed char c = source[0]; + + // Starting with letter or underscore + if( (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '_' || + (c < 0 && engine->ep.allowUnicodeIdentifiers) ) + { + tokenType = ttIdentifier; + tokenLength = 1; + + for( size_t n = 1; n < sourceLength; n++ ) + { + c = source[n]; + if( (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_' || + (c < 0 && engine->ep.allowUnicodeIdentifiers) ) + tokenLength++; + else + break; + } + + // Make sure the identifier isn't a reserved keyword + if( IsKeyWord(source, tokenLength, tokenLength, tokenType) ) + return false; + + return true; + } + + return false; +} + +bool asCTokenizer::IsKeyWord(const char *source, size_t sourceLength, size_t &tokenLength, eTokenType &tokenType) const +{ + unsigned char start = source[0]; + const sTokenWord **ptr = keywordTable[start]; + + if( !ptr ) + return false; + + for( ; *ptr; ++ptr ) + { + size_t wlen = (*ptr)->wordLength; + if( sourceLength >= wlen && strncmp(source, (*ptr)->word, wlen) == 0 ) + { + // Tokens that end with a character that can be part of an + // identifier require an extra verification to guarantee that + // we don't split an identifier token, e.g. the "!is" token + // and the tokens "!" and "isTrue" in the "!isTrue" expression. + if( wlen < sourceLength && + ((source[wlen-1] >= 'a' && source[wlen-1] <= 'z') || + (source[wlen-1] >= 'A' && source[wlen-1] <= 'Z') || + (source[wlen-1] >= '0' && source[wlen-1] <= '9')) && + ((source[wlen] >= 'a' && source[wlen] <= 'z') || + (source[wlen] >= 'A' && source[wlen] <= 'Z') || + (source[wlen] >= '0' && source[wlen] <= '9') || + (source[wlen] == '_')) ) + { + // The token doesn't really match, even though + // the start of the source matches the token + continue; + } + + tokenType = (*ptr)->tokenType; + tokenLength = wlen; + return true; + } + } + + return false; +} + +END_AS_NAMESPACE + diff --git a/3rdparty/angelscript/src/as_typeinfo.cpp b/3rdparty/angelscript/src/as_typeinfo.cpp new file mode 100644 index 0000000..13a0e1b --- /dev/null +++ b/3rdparty/angelscript/src/as_typeinfo.cpp @@ -0,0 +1,473 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2016 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_typeinfo.cpp +// + + +#include "as_config.h" +#include "as_typeinfo.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCTypeInfo::asCTypeInfo() +{ + externalRefCount.set(0); + internalRefCount.set(1); // start with one internal ref-count + engine = 0; + module = 0; + size = 0; + flags = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined + + scriptSectionIdx = -1; + declaredAt = 0; + + accessMask = 0xFFFFFFFF; + nameSpace = 0; +} + +asCTypeInfo::asCTypeInfo(asCScriptEngine *in_engine) +{ + externalRefCount.set(0); + internalRefCount.set(1); // start with one internal ref count + engine = in_engine; + module = 0; + size = 0; + flags = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined + + scriptSectionIdx = -1; + declaredAt = 0; + + accessMask = 0xFFFFFFFF; + nameSpace = engine->nameSpaces[0]; +} + +asCTypeInfo::~asCTypeInfo() +{ +} + +// interface +int asCTypeInfo::AddRef() const +{ + return externalRefCount.atomicInc(); +} + +// interface +int asCTypeInfo::Release() const +{ + int r = externalRefCount.atomicDec(); + + if (r == 0) + { + // There are no more external references, if there are also no + // internal references then it is time to delete the object type + if (internalRefCount.get() == 0) + { + // If the engine is no longer set, then it has already been + // released and we must take care of the deletion ourselves + asDELETE(const_cast(this), asCTypeInfo); + } + } + + return r; +} + +int asCTypeInfo::AddRefInternal() +{ + return internalRefCount.atomicInc(); +} + +int asCTypeInfo::ReleaseInternal() +{ + int r = internalRefCount.atomicDec(); + + if (r == 0) + { + // There are no more internal references, if there are also no + // external references then it is time to delete the object type + if (externalRefCount.get() == 0) + { + // If the engine is no longer set, then it has already been + // released and we must take care of the deletion ourselves + asDELETE(const_cast(this), asCTypeInfo); + } + } + + return r; +} + +// interface +asIScriptModule *asCTypeInfo::GetModule() const +{ + return module; +} + +void *asCTypeInfo::SetUserData(void *data, asPWORD type) +{ + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for (asUINT n = 0; n < userData.GetLength(); n += 2) + { + if (userData[n] == type) + { + void *oldData = reinterpret_cast(userData[n + 1]); + userData[n + 1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return 0; +} + +void *asCTypeInfo::GetUserData(asPWORD type) const +{ + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engine->engineRWLock); + + for (asUINT n = 0; n < userData.GetLength(); n += 2) + { + if (userData[n] == type) + { + RELEASESHARED(engine->engineRWLock); + return reinterpret_cast(userData[n + 1]); + } + } + + RELEASESHARED(engine->engineRWLock); + + return 0; +} + +// interface +const char *asCTypeInfo::GetName() const +{ + return name.AddressOf(); +} + +// interface +const char *asCTypeInfo::GetNamespace() const +{ + if( nameSpace ) + return nameSpace->name.AddressOf(); + + return 0; +} + +// interface +asDWORD asCTypeInfo::GetFlags() const +{ + return flags; +} + +// interface +asUINT asCTypeInfo::GetSize() const +{ + return size; +} + +// interface +int asCTypeInfo::GetTypeId() const +{ + if (typeId == -1) + { + // We need a non const pointer to create the asCDataType object. + // We're not breaking anything here because this function is not + // modifying the object, so this const cast is safe. + asCTypeInfo *ot = const_cast(this); + + // The engine will define the typeId for this object type + engine->GetTypeIdFromDataType(asCDataType::CreateType(ot, false)); + } + + return typeId; +} + +// interface +asIScriptEngine *asCTypeInfo::GetEngine() const +{ + return engine; +} + +// interface +const char *asCTypeInfo::GetConfigGroup() const +{ + asCConfigGroup *group = engine->FindConfigGroupForTypeInfo(this); + if (group == 0) + return 0; + + return group->groupName.AddressOf(); +} + +// interface +asDWORD asCTypeInfo::GetAccessMask() const +{ + return accessMask; +} + +// interface +int asCTypeInfo::GetProperty(asUINT index, const char **out_name, int *out_typeId, bool *out_isPrivate, bool *out_isProtected, int *out_offset, bool *out_isReference, asDWORD *out_accessMask) const +{ + UNUSED_VAR(index); + if (out_name) *out_name = 0; + if (out_typeId) *out_typeId = 0; + if (out_isPrivate) *out_isPrivate = false; + if (out_isProtected) *out_isProtected = false; + if (out_offset) *out_offset = 0; + if (out_isReference) *out_isReference = false; + if (out_accessMask) *out_accessMask = 0; + return -1; +} + +// internal +asCObjectType *CastToObjectType(asCTypeInfo *ti) +{ + // Allow call on null pointer + if (ti == 0) return 0; + + // TODO: type: Should List pattern have its own type class? + if ((ti->flags & (asOBJ_VALUE | asOBJ_REF | asOBJ_LIST_PATTERN)) && !(ti->flags & asOBJ_FUNCDEF)) + return reinterpret_cast(ti); + + return 0; +} + +// internal +asCEnumType *CastToEnumType(asCTypeInfo *ti) +{ + // Allow call on null pointer + if (ti == 0) return 0; + + if (ti->flags & (asOBJ_ENUM)) + return reinterpret_cast(ti); + + return 0; +} + +// internal +asCTypedefType *CastToTypedefType(asCTypeInfo *ti) +{ + // Allow call on null pointer + if (ti == 0) return 0; + + if (ti->flags & (asOBJ_TYPEDEF)) + return reinterpret_cast(ti); + + return 0; +} + +// internal +asCFuncdefType *CastToFuncdefType(asCTypeInfo *ti) +{ + // Allow call on null pointer + if (ti == 0) return 0; + + if (ti->flags & (asOBJ_FUNCDEF)) + return reinterpret_cast(ti); + + return 0; +} + +// internal +void asCTypeInfo::CleanUserData() +{ + asASSERT(engine); + for (asUINT n = 0; n < userData.GetLength(); n += 2) + { + if (userData[n + 1]) + { + for (asUINT c = 0; c < engine->cleanTypeInfoFuncs.GetLength(); c++) + if (engine->cleanTypeInfoFuncs[c].type == userData[n]) + engine->cleanTypeInfoFuncs[c].cleanFunc(this); + } + } + userData.SetLength(0); +} + +// internal +bool asCTypeInfo::IsShared() const +{ + // Types that can be declared by scripts need to have the explicit flag asOBJ_SHARED + if (flags & (asOBJ_SCRIPT_OBJECT | asOBJ_ENUM)) return flags & asOBJ_SHARED ? true : false; + + // Otherwise we assume the type to be shared + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +asCEnumType::~asCEnumType() +{ + asUINT n; + for (n = 0; n < enumValues.GetLength(); n++) + { + if (enumValues[n]) + asDELETE(enumValues[n], asSEnumValue); + } + enumValues.SetLength(0); +} + +// interface +asUINT asCEnumType::GetEnumValueCount() const +{ + return enumValues.GetLength(); +} + +// interface +const char *asCEnumType::GetEnumValueByIndex(asUINT index, int *outValue) const +{ + if (outValue) + *outValue = 0; + + if (index >= enumValues.GetLength()) + return 0; + + if (outValue) + *outValue = enumValues[index]->value; + + return enumValues[index]->name.AddressOf(); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +asCTypedefType::~asCTypedefType() +{ + DestroyInternal(); +} + +void asCTypedefType::DestroyInternal() +{ + if (engine == 0) return; + + // Release the object types held by the alias + if (aliasForType.GetTypeInfo()) + aliasForType.GetTypeInfo()->ReleaseInternal(); + + aliasForType = asCDataType::CreatePrimitive(ttVoid, false); + + CleanUserData(); + + // Remove the type from the engine + if (typeId != -1) + engine->RemoveFromTypeIdMap(this); + + // Clear the engine pointer to mark the object type as invalid + engine = 0; +} + +// interface +int asCTypedefType::GetTypedefTypeId() const +{ + return engine->GetTypeIdFromDataType(aliasForType); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +asCFuncdefType::asCFuncdefType(asCScriptEngine *en, asCScriptFunction *func) : asCTypeInfo(en) +{ + asASSERT(func->funcType == asFUNC_FUNCDEF); + asASSERT(func->funcdefType == 0); + + // A function pointer is special kind of reference type + flags = asOBJ_REF | asOBJ_FUNCDEF | (func->isShared ? asOBJ_SHARED : 0); + name = func->name; + nameSpace = func->nameSpace; + module = func->module; + accessMask = func->accessMask; + funcdef = func; // reference already counted by the asCScriptFunction constructor + parentClass = 0; + + func->funcdefType = this; +} + +asCFuncdefType::~asCFuncdefType() +{ + DestroyInternal(); +} + +void asCFuncdefType::DestroyInternal() +{ + if (engine == 0) return; + + // Release the funcdef + if( funcdef ) + funcdef->ReleaseInternal(); + funcdef = 0; + + // Detach from parent class + if (parentClass) + { + parentClass->childFuncDefs.RemoveValue(this); + parentClass = 0; + } + + CleanUserData(); + + // Remove the type from the engine + if (typeId != -1) + engine->RemoveFromTypeIdMap(this); + + // Clear the engine pointer to mark the object type as invalid + engine = 0; +} + +// interface +asIScriptFunction *asCFuncdefType::GetFuncdefSignature() const +{ + return funcdef; +} + +// interface +asITypeInfo *asCFuncdefType::GetParentType() const +{ + return parentClass; +} + +END_AS_NAMESPACE + + diff --git a/3rdparty/angelscript/src/as_variablescope.cpp b/3rdparty/angelscript/src/as_variablescope.cpp new file mode 100644 index 0000000..aca36da --- /dev/null +++ b/3rdparty/angelscript/src/as_variablescope.cpp @@ -0,0 +1,142 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2012 Andreas Jonsson + + 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. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_variablescope.cpp +// +// A manager class for variable declarations +// + + +#include "as_config.h" + +#ifndef AS_NO_COMPILER + +#include "as_variablescope.h" + +BEGIN_AS_NAMESPACE + +asCVariableScope::asCVariableScope(asCVariableScope *parent) +{ + this->parent = parent; + Reset(); +} + +asCVariableScope::~asCVariableScope() +{ + Reset(); +} + +void asCVariableScope::Reset() +{ + isBreakScope = false; + isContinueScope = false; + + for( asUINT n = 0; n < variables.GetLength(); n++ ) + if( variables[n] ) + { + asDELETE(variables[n],sVariable); + } + variables.SetLength(0); +} + +int asCVariableScope::DeclareVariable(const char *name, const asCDataType &type, int stackOffset, bool onHeap) +{ + // TODO: optimize: Improve linear search + // See if the variable is already declared + if( strcmp(name, "") != 0 ) + { + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->name == name ) + return -1; + } + } + + sVariable *var = asNEW(sVariable); + if( var == 0 ) + { + // Out of memory. Return without allocating the var + return -2; + } + var->name = name; + var->type = type; + var->stackOffset = stackOffset; + var->isInitialized = false; + var->isPureConstant = false; + var->onHeap = onHeap; + + // Parameters are initialized + if( stackOffset <= 0 ) + var->isInitialized = true; + + variables.PushLast(var); + + return 0; +} + +sVariable *asCVariableScope::GetVariable(const char *name) +{ + // TODO: optimize: Improve linear search + // Find the variable + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->name == name ) + return variables[n]; + } + + if( parent ) + return parent->GetVariable(name); + + return 0; +} + +sVariable *asCVariableScope::GetVariableByOffset(int offset) +{ + // TODO: optimize: Improve linear search + // Find the variable + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->stackOffset == offset ) + return variables[n]; + } + + if( parent ) + return parent->GetVariableByOffset(offset); + + return 0; +} + +END_AS_NAMESPACE + +#endif // AS_NO_COMPILER + + diff --git a/3rdparty/angelscript/src/scriptarray.cpp b/3rdparty/angelscript/src/scriptarray.cpp new file mode 100644 index 0000000..8cec62b --- /dev/null +++ b/3rdparty/angelscript/src/scriptarray.cpp @@ -0,0 +1,2051 @@ +#include +#include +#include +#include +#include // sprintf +#include + +#include "scriptarray.h" + +using namespace std; + +BEGIN_AS_NAMESPACE + +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (void)(x) + +// Set the default memory routines +// Use the angelscript engine's memory routines by default +static asALLOCFUNC_t userAlloc = asAllocMem; +static asFREEFUNC_t userFree = asFreeMem; + +// Allows the application to set which memory routines should be used by the array object +void CScriptArray::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc) +{ + userAlloc = allocFunc; + userFree = freeFunc; +} + +static void RegisterScriptArray_Native(asIScriptEngine *engine); +static void RegisterScriptArray_Generic(asIScriptEngine *engine); + +struct SArrayBuffer +{ + asDWORD maxElements; + asDWORD numElements; + asBYTE data[1]; +}; + +struct SArrayCache +{ + asIScriptFunction *cmpFunc; + asIScriptFunction *eqFunc; + int cmpFuncReturnCode; // To allow better error message in case of multiple matches + int eqFuncReturnCode; +}; + +// We just define a number here that we assume nobody else is using for +// object type user data. The add-ons have reserved the numbers 1000 +// through 1999 for this purpose, so we should be fine. +const asPWORD ARRAY_CACHE = 1000; + +static void CleanupTypeInfoArrayCache(asITypeInfo *type) +{ + SArrayCache *cache = reinterpret_cast(type->GetUserData(ARRAY_CACHE)); + if( cache ) + { + cache->~SArrayCache(); + userFree(cache); + } +} + +CScriptArray* CScriptArray::Create(asITypeInfo *ti, asUINT length) +{ + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(length, ti); + + return a; +} + +CScriptArray* CScriptArray::Create(asITypeInfo *ti, void *initList) +{ + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(ti, initList); + + return a; +} + +CScriptArray* CScriptArray::Create(asITypeInfo *ti, asUINT length, void *defVal) +{ + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(length, defVal, ti); + + return a; +} + +CScriptArray* CScriptArray::Create(asITypeInfo *ti) +{ + return CScriptArray::Create(ti, asUINT(0)); +} + +// This optional callback is called when the template type is first used by the compiler. +// It allows the application to validate if the template can be instantiated for the requested +// subtype at compile time, instead of at runtime. The output argument dontGarbageCollect +// allow the callback to tell the engine if the template instance type shouldn't be garbage collected, +// i.e. no asOBJ_GC flag. +static bool ScriptArrayTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect) +{ + // Make sure the subtype can be instantiated with a default factory/constructor, + // otherwise we won't be able to instantiate the elements. + int typeId = ti->GetSubTypeId(); + if( typeId == asTYPEID_VOID ) + return false; + if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) ) + { + asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId); + asDWORD flags = subtype->GetFlags(); + if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) ) + { + // Verify that there is a default constructor + bool found = false; + for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ ) + { + asEBehaviours beh; + asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh); + if( beh != asBEHAVE_CONSTRUCT ) continue; + + if( func->GetParamCount() == 0 ) + { + // Found the default constructor + found = true; + break; + } + } + + if( !found ) + { + // There is no default constructor + // TODO: Should format the message to give the name of the subtype for better understanding + ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor"); + return false; + } + } + else if( (flags & asOBJ_REF) ) + { + bool found = false; + + // If value assignment for ref type has been disabled then the array + // can be created if the type has a default factory function + if( !ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) ) + { + // Verify that there is a default factory + for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ ) + { + asIScriptFunction *func = subtype->GetFactoryByIndex(n); + if( func->GetParamCount() == 0 ) + { + // Found the default factory + found = true; + break; + } + } + } + + if( !found ) + { + // No default factory + // TODO: Should format the message to give the name of the subtype for better understanding + ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory"); + return false; + } + } + + // If the object type is not garbage collected then the array also doesn't need to be + if( !(flags & asOBJ_GC) ) + dontGarbageCollect = true; + } + else if( !(typeId & asTYPEID_OBJHANDLE) ) + { + // Arrays with primitives cannot form circular references, + // thus there is no need to garbage collect them + dontGarbageCollect = true; + } + else + { + assert( typeId & asTYPEID_OBJHANDLE ); + + // It is not necessary to set the array as garbage collected for all handle types. + // If it is possible to determine that the handle cannot refer to an object type + // that can potentially form a circular reference with the array then it is not + // necessary to make the array garbage collected. + asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId); + asDWORD flags = subtype->GetFlags(); + if( !(flags & asOBJ_GC) ) + { + if( (flags & asOBJ_SCRIPT_OBJECT) ) + { + // Even if a script class is by itself not garbage collected, it is possible + // that classes that derive from it may be, so it is not possible to know + // that no circular reference can occur. + if( (flags & asOBJ_NOINHERIT) ) + { + // A script class declared as final cannot be inherited from, thus + // we can be certain that the object cannot be garbage collected. + dontGarbageCollect = true; + } + } + else + { + // For application registered classes we assume the application knows + // what it is doing and don't mark the array as garbage collected unless + // the type is also garbage collected. + dontGarbageCollect = true; + } + } + } + + // The type is ok + return true; +} + +// Registers the template array type +void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0 ) + RegisterScriptArray_Native(engine); + else + RegisterScriptArray_Generic(engine); + + if( defaultArray ) + { + int r = engine->RegisterDefaultArrayType("array"); assert( r >= 0 ); + UNUSED_VAR(r); + } +} + +static void RegisterScriptArray_Native(asIScriptEngine *engine) +{ + int r = 0; + UNUSED_VAR(r); + + // Register the object type user data clean up + engine->SetTypeInfoUserDataCleanupCallback(CleanupTypeInfoArrayCache, ARRAY_CACHE); + + // Register the array type as a template + r = engine->RegisterObjectType("array", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 ); + + // Register a callback for validating the subtype before it is used + r = engine->RegisterObjectBehaviour("array", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL); assert( r >= 0 ); + + // Templates receive the object type as the first parameter. To the script writer this is hidden + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTIONPR(CScriptArray::Create, (asITypeInfo*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint length)", asFUNCTIONPR(CScriptArray::Create, (asITypeInfo*, asUINT), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint length, const T &in value)", asFUNCTIONPR(CScriptArray::Create, (asITypeInfo*, asUINT, void *), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + + // Register the factory that will be used for initialization lists + r = engine->RegisterObjectBehaviour("array", asBEHAVE_LIST_FACTORY, "array@ f(int&in type, int&in list) {repeat T}", asFUNCTIONPR(CScriptArray::Create, (asITypeInfo*, void*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + + // The memory management methods + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL); assert( r >= 0 ); + + // The index operator returns the template subtype + r = engine->RegisterObjectMethod("array", "T &opIndex(uint index)", asMETHODPR(CScriptArray, At, (asUINT), void*), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "const T &opIndex(uint index) const", asMETHODPR(CScriptArray, At, (asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 ); + + // The assignment operator + r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL); assert( r >= 0 ); + + // Other methods + r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const T&in value)", asMETHODPR(CScriptArray, InsertAt, (asUINT, void *), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const array& arr)", asMETHODPR(CScriptArray, InsertAt, (asUINT, const CScriptArray &), void), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void insertLast(const T&in value)", asMETHOD(CScriptArray, InsertLast), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void removeAt(uint index)", asMETHOD(CScriptArray, RemoveAt), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void removeLast()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void removeRange(uint start, uint count)", asMETHOD(CScriptArray, RemoveRange), asCALL_THISCALL); assert(r >= 0); + // TODO: Should length() and resize() be deprecated as the property accessors do the same thing? + r = engine->RegisterObjectMethod("array", "uint length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void reserve(uint length)", asMETHOD(CScriptArray, Reserve), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void resize(uint length)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortAsc()", asMETHODPR(CScriptArray, SortAsc, (), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortAsc(uint startAt, uint count)", asMETHODPR(CScriptArray, SortAsc, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortDesc()", asMETHODPR(CScriptArray, SortDesc, (), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortDesc(uint startAt, uint count)", asMETHODPR(CScriptArray, SortDesc, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void reverse()", asMETHOD(CScriptArray, Reverse), asCALL_THISCALL); assert( r >= 0 ); + // The token 'if_handle_then_const' tells the engine that if the type T is a handle, then it should refer to a read-only object + r = engine->RegisterObjectMethod("array", "int find(const T&in if_handle_then_const value) const", asMETHODPR(CScriptArray, Find, (void*) const, int), asCALL_THISCALL); assert( r >= 0 ); + // TODO: It should be "int find(const T&in value, uint startAt = 0) const" + r = engine->RegisterObjectMethod("array", "int find(uint startAt, const T&in if_handle_then_const value) const", asMETHODPR(CScriptArray, Find, (asUINT, void*) const, int), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "int findByRef(const T&in if_handle_then_const value) const", asMETHODPR(CScriptArray, FindByRef, (void*) const, int), asCALL_THISCALL); assert( r >= 0 ); + // TODO: It should be "int findByRef(const T&in value, uint startAt = 0) const" + r = engine->RegisterObjectMethod("array", "int findByRef(uint startAt, const T&in if_handle_then_const value) const", asMETHODPR(CScriptArray, FindByRef, (asUINT, void*) const, int), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "bool opEquals(const array&in) const", asMETHOD(CScriptArray, operator==), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "bool isEmpty() const", asMETHOD(CScriptArray, IsEmpty), asCALL_THISCALL); assert( r >= 0 ); + + // Register virtual properties + r = engine->RegisterObjectMethod("array", "uint get_length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void set_length(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL); assert( r >= 0 ); + + // Register GC behaviours in case the array needs to be garbage collected + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptArray, GetRefCount), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptArray, SetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptArray, GetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptArray, EnumReferences), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptArray, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 ); + +#if AS_USE_STLNAMES == 1 + // Same as length + r = engine->RegisterObjectMethod("array", "uint size() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL); assert( r >= 0 ); + // Same as isEmpty + r = engine->RegisterObjectMethod("array", "bool empty() const", asMETHOD(CScriptArray, IsEmpty), asCALL_THISCALL); assert( r >= 0 ); + // Same as insertLast + r = engine->RegisterObjectMethod("array", "void push_back(const T&in)", asMETHOD(CScriptArray, InsertLast), asCALL_THISCALL); assert( r >= 0 ); + // Same as removeLast + r = engine->RegisterObjectMethod("array", "void pop_back()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL); assert( r >= 0 ); + // Same as insertAt + r = engine->RegisterObjectMethod("array", "void insert(uint index, const T&in value)", asMETHODPR(CScriptArray, InsertAt, (asUINT, void *), void), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void insert(uint index, const array& arr)", asMETHODPR(CScriptArray, InsertAt, (asUINT, const CScriptArray &), void), asCALL_THISCALL); assert(r >= 0); + // Same as removeAt + r = engine->RegisterObjectMethod("array", "void erase(uint)", asMETHOD(CScriptArray, RemoveAt), asCALL_THISCALL); assert( r >= 0 ); +#endif +} + +CScriptArray &CScriptArray::operator=(const CScriptArray &other) +{ + // Only perform the copy if the array types are the same + if( &other != this && + other.GetArrayObjectType() == GetArrayObjectType() ) + { + // Make sure the arrays are of the same size + Resize(other.buffer->numElements); + + // Copy the value of each element + CopyBuffer(buffer, other.buffer); + } + + return *this; +} + +CScriptArray::CScriptArray(asITypeInfo *ti, void *buf) +{ + // The object type should be the template instance of the array + assert( ti && string(ti->GetName()) == "array" ); + + refCount = 1; + gcFlag = false; + objType = ti; + objType->AddRef(); + buffer = 0; + + Precache(); + + asIScriptEngine *engine = ti->GetEngine(); + + // Determine element size + if( subTypeId & asTYPEID_MASK_OBJECT ) + elementSize = sizeof(asPWORD); + else + elementSize = engine->GetSizeOfPrimitiveType(subTypeId); + + // Determine the initial size from the buffer + asUINT length = *(asUINT*)buf; + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(length) ) + { + // Don't continue with the initialization + return; + } + + // Copy the values of the array elements from the buffer + if( (ti->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 ) + { + CreateBuffer(&buffer, length); + + // Copy the values of the primitive type into the internal buffer + if( length > 0 ) + memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); + } + else if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE ) + { + CreateBuffer(&buffer, length); + + // Copy the handles into the internal buffer + if( length > 0 ) + memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); + + // With object handles it is safe to clear the memory in the received buffer + // instead of increasing the ref count. It will save time both by avoiding the + // call the increase ref, and also relieve the engine from having to release + // its references too + memset((((asUINT*)buf)+1), 0, length * elementSize); + } + else if( ti->GetSubType()->GetFlags() & asOBJ_REF ) + { + // Only allocate the buffer, but not the objects + subTypeId |= asTYPEID_OBJHANDLE; + CreateBuffer(&buffer, length); + subTypeId &= ~asTYPEID_OBJHANDLE; + + // Copy the handles into the internal buffer + if( length > 0 ) + memcpy(buffer->data, (((asUINT*)buf)+1), length * elementSize); + + // For ref types we can do the same as for handles, as they are + // implicitly stored as handles. + memset((((asUINT*)buf)+1), 0, length * elementSize); + } + else + { + // TODO: Optimize by calling the copy constructor of the object instead of + // constructing with the default constructor and then assigning the value + // TODO: With C++11 ideally we should be calling the move constructor, instead + // of the copy constructor as the engine will just discard the objects in the + // buffer afterwards. + CreateBuffer(&buffer, length); + + // For value types we need to call the opAssign for each individual object + for( asUINT n = 0; n < length; n++ ) + { + void *obj = At(n); + asBYTE *srcObj = (asBYTE*)buf; + srcObj += 4 + n*ti->GetSubType()->GetSize(); + engine->AssignScriptObject(obj, srcObj, ti->GetSubType()); + } + } + + // Notify the GC of the successful creation + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType); +} + +CScriptArray::CScriptArray(asUINT length, asITypeInfo *ti) +{ + // The object type should be the template instance of the array + assert( ti && string(ti->GetName()) == "array" ); + + refCount = 1; + gcFlag = false; + objType = ti; + objType->AddRef(); + buffer = 0; + + Precache(); + + // Determine element size + if( subTypeId & asTYPEID_MASK_OBJECT ) + elementSize = sizeof(asPWORD); + else + elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId); + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(length) ) + { + // Don't continue with the initialization + return; + } + + CreateBuffer(&buffer, length); + + // Notify the GC of the successful creation + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType); +} + +CScriptArray::CScriptArray(const CScriptArray &other) +{ + refCount = 1; + gcFlag = false; + objType = other.objType; + objType->AddRef(); + buffer = 0; + + Precache(); + + elementSize = other.elementSize; + + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType); + + CreateBuffer(&buffer, 0); + + // Copy the content + *this = other; +} + +CScriptArray::CScriptArray(asUINT length, void *defVal, asITypeInfo *ti) +{ + // The object type should be the template instance of the array + assert( ti && string(ti->GetName()) == "array" ); + + refCount = 1; + gcFlag = false; + objType = ti; + objType->AddRef(); + buffer = 0; + + Precache(); + + // Determine element size + if( subTypeId & asTYPEID_MASK_OBJECT ) + elementSize = sizeof(asPWORD); + else + elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId); + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(length) ) + { + // Don't continue with the initialization + return; + } + + CreateBuffer(&buffer, length); + + // Notify the GC of the successful creation + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType); + + // Initialize the elements with the default value + for( asUINT n = 0; n < GetSize(); n++ ) + SetValue(n, defVal); +} + +void CScriptArray::SetValue(asUINT index, void *value) +{ + // At() will take care of the out-of-bounds checking, though + // if called from the application then nothing will be done + void *ptr = At(index); + if( ptr == 0 ) return; + + if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) ) + objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType()); + else if( subTypeId & asTYPEID_OBJHANDLE ) + { + void *tmp = *(void**)ptr; + *(void**)ptr = *(void**)value; + objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType()); + if( tmp ) + objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType()); + } + else if( subTypeId == asTYPEID_BOOL || + subTypeId == asTYPEID_INT8 || + subTypeId == asTYPEID_UINT8 ) + *(char*)ptr = *(char*)value; + else if( subTypeId == asTYPEID_INT16 || + subTypeId == asTYPEID_UINT16 ) + *(short*)ptr = *(short*)value; + else if( subTypeId == asTYPEID_INT32 || + subTypeId == asTYPEID_UINT32 || + subTypeId == asTYPEID_FLOAT || + subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles + *(int*)ptr = *(int*)value; + else if( subTypeId == asTYPEID_INT64 || + subTypeId == asTYPEID_UINT64 || + subTypeId == asTYPEID_DOUBLE ) + *(double*)ptr = *(double*)value; +} + +CScriptArray::~CScriptArray() +{ + if( buffer ) + { + DeleteBuffer(buffer); + buffer = 0; + } + if( objType ) objType->Release(); +} + +asUINT CScriptArray::GetSize() const +{ + return buffer->numElements; +} + +bool CScriptArray::IsEmpty() const +{ + return buffer->numElements == 0; +} + +void CScriptArray::Reserve(asUINT maxElements) +{ + if( maxElements <= buffer->maxElements ) + return; + + if( !CheckMaxSize(maxElements) ) + return; + + // Allocate memory for the buffer + SArrayBuffer *newBuffer = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*maxElements)); + if( newBuffer ) + { + newBuffer->numElements = buffer->numElements; + newBuffer->maxElements = maxElements; + } + else + { + // Out of memory + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + return; + } + + // As objects in arrays of objects are not stored inline, it is safe to use memcpy here + // since we're just copying the pointers to objects and not the actual objects. + memcpy(newBuffer->data, buffer->data, buffer->numElements*elementSize); + + // Release the old buffer + userFree(buffer); + + buffer = newBuffer; +} + +void CScriptArray::Resize(asUINT numElements) +{ + if( !CheckMaxSize(numElements) ) + return; + + Resize((int)numElements - (int)buffer->numElements, (asUINT)-1); +} + +void CScriptArray::RemoveRange(asUINT start, asUINT count) +{ + if (count == 0) + return; + + if( buffer == 0 || start > buffer->numElements ) + { + // If this is called from a script we raise a script exception + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + ctx->SetException("Index out of bounds"); + return; + } + + // Cap count to the end of the array + if (start + count > buffer->numElements) + count = buffer->numElements - start; + + // Destroy the elements that are being removed + Destruct(buffer, start, start + count); + + // Compact the elements + // As objects in arrays of objects are not stored inline, it is safe to use memmove here + // since we're just copying the pointers to objects and not the actual objects. + memmove(buffer->data + start*elementSize, buffer->data + (start + count)*elementSize, count*elementSize); + buffer->numElements -= count; +} + +// Internal +void CScriptArray::Resize(int delta, asUINT at) +{ + if( delta < 0 ) + { + if( -delta > (int)buffer->numElements ) + delta = -(int)buffer->numElements; + if( at > buffer->numElements + delta ) + at = buffer->numElements + delta; + } + else if( delta > 0 ) + { + // Make sure the array size isn't too large for us to handle + if( delta > 0 && !CheckMaxSize(buffer->numElements + delta) ) + return; + + if( at > buffer->numElements ) + at = buffer->numElements; + } + + if( delta == 0 ) return; + + if( buffer->maxElements < buffer->numElements + delta ) + { + // Allocate memory for the buffer + SArrayBuffer *newBuffer = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta))); + if( newBuffer ) + { + newBuffer->numElements = buffer->numElements + delta; + newBuffer->maxElements = newBuffer->numElements; + } + else + { + // Out of memory + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + return; + } + + // As objects in arrays of objects are not stored inline, it is safe to use memcpy here + // since we're just copying the pointers to objects and not the actual objects. + memcpy(newBuffer->data, buffer->data, at*elementSize); + if( at < buffer->numElements ) + memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize); + + // Initialize the new elements with default values + Construct(newBuffer, at, at+delta); + + // Release the old buffer + userFree(buffer); + + buffer = newBuffer; + } + else if( delta < 0 ) + { + Destruct(buffer, at, at-delta); + // As objects in arrays of objects are not stored inline, it is safe to use memmove here + // since we're just copying the pointers to objects and not the actual objects. + memmove(buffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements - (at-delta))*elementSize); + buffer->numElements += delta; + } + else + { + // As objects in arrays of objects are not stored inline, it is safe to use memmove here + // since we're just copying the pointers to objects and not the actual objects. + memmove(buffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements - at)*elementSize); + Construct(buffer, at, at+delta); + buffer->numElements += delta; + } +} + +// internal +bool CScriptArray::CheckMaxSize(asUINT numElements) +{ + // This code makes sure the size of the buffer that is allocated + // for the array doesn't overflow and becomes smaller than requested + + asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1; + if( elementSize > 0 ) + maxSize /= elementSize; + + if( numElements > maxSize ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Too large array size"); + + return false; + } + + // OK + return true; +} + +asITypeInfo *CScriptArray::GetArrayObjectType() const +{ + return objType; +} + +int CScriptArray::GetArrayTypeId() const +{ + return objType->GetTypeId(); +} + +int CScriptArray::GetElementTypeId() const +{ + return subTypeId; +} + +void CScriptArray::InsertAt(asUINT index, void *value) +{ + if( index > buffer->numElements ) + { + // If this is called from a script we raise a script exception + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Index out of bounds"); + return; + } + + // Make room for the new element + Resize(1, index); + + // Set the value of the new element + SetValue(index, value); +} + +void CScriptArray::InsertAt(asUINT index, const CScriptArray &arr) +{ + if (index > buffer->numElements) + { + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + ctx->SetException("Index out of bounds"); + return; + } + + if (objType != arr.objType) + { + // This shouldn't really be possible to happen when + // called from a script, but let's check for it anyway + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + ctx->SetException("Mismatching array types"); + return; + } + + asUINT elements = arr.GetSize(); + Resize(elements, index); + if (&arr != this) + { + for (asUINT n = 0; n < arr.GetSize(); n++) + { + // This const cast is allowed, since we know the + // value will only be used to make a copy of it + void *value = const_cast(arr.At(n)); + SetValue(index + n, value); + } + } + else + { + // The array that is being inserted is the same as this one. + // So we should iterate over the elements before the index, + // and then the elements after + for (asUINT n = 0; n < index; n++) + { + // This const cast is allowed, since we know the + // value will only be used to make a copy of it + void *value = const_cast(arr.At(n)); + SetValue(index + n, value); + } + + for (asUINT n = index + elements, m = 0; n < arr.GetSize(); n++, m++) + { + // This const cast is allowed, since we know the + // value will only be used to make a copy of it + void *value = const_cast(arr.At(n)); + SetValue(index + index + m, value); + } + } +} + +void CScriptArray::InsertLast(void *value) +{ + InsertAt(buffer->numElements, value); +} + +void CScriptArray::RemoveAt(asUINT index) +{ + if( index >= buffer->numElements ) + { + // If this is called from a script we raise a script exception + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Index out of bounds"); + return; + } + + // Remove the element + Resize(-1, index); +} + +void CScriptArray::RemoveLast() +{ + RemoveAt(buffer->numElements-1); +} + +// Return a pointer to the array element. Returns 0 if the index is out of bounds +const void *CScriptArray::At(asUINT index) const +{ + if( buffer == 0 || index >= buffer->numElements ) + { + // If this is called from a script we raise a script exception + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Index out of bounds"); + return 0; + } + + if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) ) + return *(void**)(buffer->data + elementSize*index); + else + return buffer->data + elementSize*index; +} +void *CScriptArray::At(asUINT index) +{ + return const_cast(const_cast(this)->At(index)); +} + +void *CScriptArray::GetBuffer() +{ + return buffer->data; +} + + +// internal +void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements) +{ + *buf = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1+elementSize*numElements)); + + if( *buf ) + { + (*buf)->numElements = numElements; + (*buf)->maxElements = numElements; + Construct(*buf, 0, numElements); + } + else + { + // Oops, out of memory + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Out of memory"); + } +} + +// internal +void CScriptArray::DeleteBuffer(SArrayBuffer *buf) +{ + Destruct(buf, 0, buf->numElements); + + // Free the buffer + userFree(buf); +} + +// internal +void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end) +{ + if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) ) + { + // Create an object using the default constructor/factory for each element + void **max = (void**)(buf->data + end * sizeof(void*)); + void **d = (void**)(buf->data + start * sizeof(void*)); + + asIScriptEngine *engine = objType->GetEngine(); + asITypeInfo *subType = objType->GetSubType(); + + for( ; d < max; d++ ) + { + *d = (void*)engine->CreateScriptObject(subType); + if( *d == 0 ) + { + // Set the remaining entries to null so the destructor + // won't attempt to destroy invalid objects later + memset(d, 0, sizeof(void*)*(max-d)); + + // There is no need to set an exception on the context, + // as CreateScriptObject has already done that + return; + } + } + } + else + { + // Set all elements to zero whether they are handles or primitives + void *d = (void*)(buf->data + start * elementSize); + memset(d, 0, (end-start)*elementSize); + } +} + +// internal +void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end) +{ + if( subTypeId & asTYPEID_MASK_OBJECT ) + { + asIScriptEngine *engine = objType->GetEngine(); + + void **max = (void**)(buf->data + end * sizeof(void*)); + void **d = (void**)(buf->data + start * sizeof(void*)); + + for( ; d < max; d++ ) + { + if( *d ) + engine->ReleaseScriptObject(*d, objType->GetSubType()); + } + } +} + + +// internal +bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext *ctx, SArrayCache *cache) +{ + if( !asc ) + { + // Swap items + const void *TEMP = a; + a = b; + b = TEMP; + } + + if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) ) + { + // Simple compare of values + switch( subTypeId ) + { + #define COMPARE(T) *((T*)a) < *((T*)b) + case asTYPEID_BOOL: return COMPARE(bool); + case asTYPEID_INT8: return COMPARE(signed char); + case asTYPEID_UINT8: return COMPARE(unsigned char); + case asTYPEID_INT16: return COMPARE(signed short); + case asTYPEID_UINT16: return COMPARE(unsigned short); + case asTYPEID_INT32: return COMPARE(signed int); + case asTYPEID_UINT32: return COMPARE(unsigned int); + case asTYPEID_FLOAT: return COMPARE(float); + case asTYPEID_DOUBLE: return COMPARE(double); + default: return COMPARE(signed int); // All enums fall in this case + #undef COMPARE + } + } + else + { + int r = 0; + + if( subTypeId & asTYPEID_OBJHANDLE ) + { + // Allow sort to work even if the array contains null handles + if( *(void**)a == 0 ) return true; + if( *(void**)b == 0 ) return false; + } + + // Execute object opCmp + if( cache && cache->cmpFunc ) + { + // TODO: Add proper error handling + r = ctx->Prepare(cache->cmpFunc); assert(r >= 0); + + if( subTypeId & asTYPEID_OBJHANDLE ) + { + r = ctx->SetObject(*((void**)a)); assert(r >= 0); + r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0); + } + else + { + r = ctx->SetObject((void*)a); assert(r >= 0); + r = ctx->SetArgObject(0, (void*)b); assert(r >= 0); + } + + r = ctx->Execute(); + + if( r == asEXECUTION_FINISHED ) + { + return (int)ctx->GetReturnDWord() < 0; + } + } + } + + return false; +} + +void CScriptArray::Reverse() +{ + asUINT size = GetSize(); + + if( size >= 2 ) + { + asBYTE TEMP[16]; + + for( asUINT i = 0; i < size / 2; i++ ) + { + Copy(TEMP, GetArrayItemPointer(i)); + Copy(GetArrayItemPointer(i), GetArrayItemPointer(size - i - 1)); + Copy(GetArrayItemPointer(size - i - 1), TEMP); + } + } +} + +bool CScriptArray::operator==(const CScriptArray &other) const +{ + if( objType != other.objType ) + return false; + + if( GetSize() != other.GetSize() ) + return false; + + asIScriptContext *cmpContext = 0; + bool isNested = false; + + if( subTypeId & ~asTYPEID_MASK_SEQNBR ) + { + // Try to reuse the active context + cmpContext = asGetActiveContext(); + if( cmpContext ) + { + if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 ) + isNested = true; + else + cmpContext = 0; + } + if( cmpContext == 0 ) + { + // TODO: Ideally this context would be retrieved from a pool, so we don't have to + // create a new one everytime. We could keep a context with the array object + // but that would consume a lot of resources as each context is quite heavy. + cmpContext = objType->GetEngine()->CreateContext(); + } + } + + // Check if all elements are equal + bool isEqual = true; + SArrayCache *cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); + for( asUINT n = 0; n < GetSize(); n++ ) + if( !Equals(At(n), other.At(n), cmpContext, cache) ) + { + isEqual = false; + break; + } + + if( cmpContext ) + { + if( isNested ) + { + asEContextState state = cmpContext->GetState(); + cmpContext->PopState(); + if( state == asEXECUTION_ABORTED ) + cmpContext->Abort(); + } + else + cmpContext->Release(); + } + + return isEqual; +} + +// internal +bool CScriptArray::Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const +{ + if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) ) + { + // Simple compare of values + switch( subTypeId ) + { + #define COMPARE(T) *((T*)a) == *((T*)b) + case asTYPEID_BOOL: return COMPARE(bool); + case asTYPEID_INT8: return COMPARE(signed char); + case asTYPEID_UINT8: return COMPARE(unsigned char); + case asTYPEID_INT16: return COMPARE(signed short); + case asTYPEID_UINT16: return COMPARE(unsigned short); + case asTYPEID_INT32: return COMPARE(signed int); + case asTYPEID_UINT32: return COMPARE(unsigned int); + case asTYPEID_FLOAT: return COMPARE(float); + case asTYPEID_DOUBLE: return COMPARE(double); + default: return COMPARE(signed int); // All enums fall here + #undef COMPARE + } + } + else + { + int r = 0; + + if( subTypeId & asTYPEID_OBJHANDLE ) + { + // Allow the find to work even if the array contains null handles + if( *(void**)a == *(void**)b ) return true; + } + + // Execute object opEquals if available + if( cache && cache->eqFunc ) + { + // TODO: Add proper error handling + r = ctx->Prepare(cache->eqFunc); assert(r >= 0); + + if( subTypeId & asTYPEID_OBJHANDLE ) + { + r = ctx->SetObject(*((void**)a)); assert(r >= 0); + r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0); + } + else + { + r = ctx->SetObject((void*)a); assert(r >= 0); + r = ctx->SetArgObject(0, (void*)b); assert(r >= 0); + } + + r = ctx->Execute(); + + if( r == asEXECUTION_FINISHED ) + return ctx->GetReturnByte() != 0; + + return false; + } + + // Execute object opCmp if available + if( cache && cache->cmpFunc ) + { + // TODO: Add proper error handling + r = ctx->Prepare(cache->cmpFunc); assert(r >= 0); + + if( subTypeId & asTYPEID_OBJHANDLE ) + { + r = ctx->SetObject(*((void**)a)); assert(r >= 0); + r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0); + } + else + { + r = ctx->SetObject((void*)a); assert(r >= 0); + r = ctx->SetArgObject(0, (void*)b); assert(r >= 0); + } + + r = ctx->Execute(); + + if( r == asEXECUTION_FINISHED ) + return (int)ctx->GetReturnDWord() == 0; + + return false; + } + } + + return false; +} + +int CScriptArray::FindByRef(void *ref) const +{ + return FindByRef(0, ref); +} + +int CScriptArray::FindByRef(asUINT startAt, void *ref) const +{ + // Find the matching element by its reference + asUINT size = GetSize(); + if( subTypeId & asTYPEID_OBJHANDLE ) + { + // Dereference the pointer + ref = *(void**)ref; + for( asUINT i = startAt; i < size; i++ ) + { + if( *(void**)At(i) == ref ) + return i; + } + } + else + { + // Compare the reference directly + for( asUINT i = startAt; i < size; i++ ) + { + if( At(i) == ref ) + return i; + } + } + + return -1; +} + +int CScriptArray::Find(void *value) const +{ + return Find(0, value); +} + +int CScriptArray::Find(asUINT startAt, void *value) const +{ + // Check if the subtype really supports find() + // TODO: Can't this be done at compile time too by the template callback + SArrayCache *cache = 0; + if( subTypeId & ~asTYPEID_MASK_SEQNBR ) + { + cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); + if( !cache || (cache->cmpFunc == 0 && cache->eqFunc == 0) ) + { + asIScriptContext *ctx = asGetActiveContext(); + asITypeInfo* subType = objType->GetEngine()->GetTypeInfoById(subTypeId); + + // Throw an exception + if( ctx ) + { + char tmp[512]; + + if( cache && cache->eqFuncReturnCode == asMULTIPLE_FUNCTIONS ) +#if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__) + sprintf_s(tmp, 512, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName()); +#else + sprintf(tmp, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName()); +#endif + else +#if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__) + sprintf_s(tmp, 512, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName()); +#else + sprintf(tmp, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName()); +#endif + ctx->SetException(tmp); + } + + return -1; + } + } + + asIScriptContext *cmpContext = 0; + bool isNested = false; + + if( subTypeId & ~asTYPEID_MASK_SEQNBR ) + { + // Try to reuse the active context + cmpContext = asGetActiveContext(); + if( cmpContext ) + { + if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 ) + isNested = true; + else + cmpContext = 0; + } + if( cmpContext == 0 ) + { + // TODO: Ideally this context would be retrieved from a pool, so we don't have to + // create a new one everytime. We could keep a context with the array object + // but that would consume a lot of resources as each context is quite heavy. + cmpContext = objType->GetEngine()->CreateContext(); + } + } + + // Find the matching element + int ret = -1; + asUINT size = GetSize(); + + for( asUINT i = startAt; i < size; i++ ) + { + // value passed by reference + if( Equals(At(i), value, cmpContext, cache) ) + { + ret = (int)i; + break; + } + } + + if( cmpContext ) + { + if( isNested ) + { + asEContextState state = cmpContext->GetState(); + cmpContext->PopState(); + if( state == asEXECUTION_ABORTED ) + cmpContext->Abort(); + } + else + cmpContext->Release(); + } + + return ret; +} + + + +// internal +// Copy object handle or primitive value +// Even in arrays of objects the objects are allocated on +// the heap and the array stores the pointers to the objects +void CScriptArray::Copy(void *dst, void *src) +{ + memcpy(dst, src, elementSize); +} + + +// internal +// Return pointer to array item (object handle or primitive value) +void *CScriptArray::GetArrayItemPointer(int index) +{ + return buffer->data + index * elementSize; +} + +// internal +// Return pointer to data in buffer (object or primitive) +void *CScriptArray::GetDataPointer(void *buf) +{ + if ((subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) ) + { + // Real address of object + return reinterpret_cast(*(size_t*)buf); + } + else + { + // Primitive is just a raw data + return buf; + } +} + + +// Sort ascending +void CScriptArray::SortAsc() +{ + Sort(0, GetSize(), true); +} + +// Sort ascending +void CScriptArray::SortAsc(asUINT startAt, asUINT count) +{ + Sort(startAt, count, true); +} + +// Sort descending +void CScriptArray::SortDesc() +{ + Sort(0, GetSize(), false); +} + +// Sort descending +void CScriptArray::SortDesc(asUINT startAt, asUINT count) +{ + Sort(startAt, count, false); +} + + +// internal +void CScriptArray::Sort(asUINT startAt, asUINT count, bool asc) +{ + // Subtype isn't primitive and doesn't have opCmp + SArrayCache *cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); + if( subTypeId & ~asTYPEID_MASK_SEQNBR ) + { + if( !cache || cache->cmpFunc == 0 ) + { + asIScriptContext *ctx = asGetActiveContext(); + asITypeInfo* subType = objType->GetEngine()->GetTypeInfoById(subTypeId); + + // Throw an exception + if( ctx ) + { + char tmp[512]; + + if( cache && cache->cmpFuncReturnCode == asMULTIPLE_FUNCTIONS ) +#if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__) + sprintf_s(tmp, 512, "Type '%s' has multiple matching opCmp methods", subType->GetName()); +#else + sprintf(tmp, "Type '%s' has multiple matching opCmp methods", subType->GetName()); +#endif + else +#if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__) + sprintf_s(tmp, 512, "Type '%s' does not have a matching opCmp method", subType->GetName()); +#else + sprintf(tmp, "Type '%s' does not have a matching opCmp method", subType->GetName()); +#endif + + ctx->SetException(tmp); + } + + return; + } + } + + // No need to sort + if( count < 2 ) + { + return; + } + + int start = startAt; + int end = startAt + count; + + // Check if we could access invalid item while sorting + if( start >= (int)buffer->numElements || end > (int)buffer->numElements ) + { + asIScriptContext *ctx = asGetActiveContext(); + + // Throw an exception + if( ctx ) + { + ctx->SetException("Index out of bounds"); + } + + return; + } + + asBYTE tmp[16]; + asIScriptContext *cmpContext = 0; + bool isNested = false; + + if( subTypeId & ~asTYPEID_MASK_SEQNBR ) + { + // Try to reuse the active context + cmpContext = asGetActiveContext(); + if( cmpContext ) + { + if( cmpContext->GetEngine() == objType->GetEngine() && cmpContext->PushState() >= 0 ) + isNested = true; + else + cmpContext = 0; + } + if( cmpContext == 0 ) + { + // TODO: Ideally this context would be retrieved from a pool, so we don't have to + // create a new one everytime. We could keep a context with the array object + // but that would consume a lot of resources as each context is quite heavy. + cmpContext = objType->GetEngine()->CreateContext(); + } + } + + // Insertion sort + for( int i = start + 1; i < end; i++ ) + { + Copy(tmp, GetArrayItemPointer(i)); + + int j = i - 1; + + while( j >= start && Less(GetDataPointer(tmp), At(j), asc, cmpContext, cache) ) + { + Copy(GetArrayItemPointer(j + 1), GetArrayItemPointer(j)); + j--; + } + + Copy(GetArrayItemPointer(j + 1), tmp); + } + + if( cmpContext ) + { + if( isNested ) + { + asEContextState state = cmpContext->GetState(); + cmpContext->PopState(); + if( state == asEXECUTION_ABORTED ) + cmpContext->Abort(); + } + else + cmpContext->Release(); + } +} + +// internal +void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src) +{ + asIScriptEngine *engine = objType->GetEngine(); + if( subTypeId & asTYPEID_OBJHANDLE ) + { + // Copy the references and increase the reference counters + if( dst->numElements > 0 && src->numElements > 0 ) + { + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + + void **max = (void**)(dst->data + count * sizeof(void*)); + void **d = (void**)dst->data; + void **s = (void**)src->data; + + for( ; d < max; d++, s++ ) + { + void *tmp = *d; + *d = *s; + if( *d ) + engine->AddRefScriptObject(*d, objType->GetSubType()); + // Release the old ref after incrementing the new to avoid problem incase it is the same ref + if( tmp ) + engine->ReleaseScriptObject(tmp, objType->GetSubType()); + } + } + } + else + { + if( dst->numElements > 0 && src->numElements > 0 ) + { + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + if( subTypeId & asTYPEID_MASK_OBJECT ) + { + // Call the assignment operator on all of the objects + void **max = (void**)(dst->data + count * sizeof(void*)); + void **d = (void**)dst->data; + void **s = (void**)src->data; + + asITypeInfo *subType = objType->GetSubType(); + for( ; d < max; d++, s++ ) + engine->AssignScriptObject(*d, *s, subType); + } + else + { + // Primitives are copied byte for byte + memcpy(dst->data, src->data, count*elementSize); + } + } + } +} + +// internal +// Precache some info +void CScriptArray::Precache() +{ + subTypeId = objType->GetSubTypeId(); + + // Check if it is an array of objects. Only for these do we need to cache anything + // Type ids for primitives and enums only has the sequence number part + if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) ) + return; + + // The opCmp and opEquals methods are cached because the searching for the + // methods is quite time consuming if a lot of array objects are created. + + // First check if a cache already exists for this array type + SArrayCache *cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); + if( cache ) return; + + // We need to make sure the cache is created only once, even + // if multiple threads reach the same point at the same time + asAcquireExclusiveLock(); + + // Now that we got the lock, we need to check again to make sure the + // cache wasn't created while we were waiting for the lock + cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); + if( cache ) + { + asReleaseExclusiveLock(); + return; + } + + // Create the cache + cache = reinterpret_cast(userAlloc(sizeof(SArrayCache))); + memset(cache, 0, sizeof(SArrayCache)); + + // If the sub type is a handle to const, then the methods must be const too + bool mustBeConst = (subTypeId & asTYPEID_HANDLETOCONST) ? true : false; + + asITypeInfo *subType = objType->GetEngine()->GetTypeInfoById(subTypeId); + if( subType ) + { + for( asUINT i = 0; i < subType->GetMethodCount(); i++ ) + { + asIScriptFunction *func = subType->GetMethodByIndex(i); + + if( func->GetParamCount() == 1 && (!mustBeConst || func->IsReadOnly()) ) + { + asDWORD flags = 0; + int returnTypeId = func->GetReturnTypeId(&flags); + + // The method must not return a reference + if( flags != asTM_NONE ) + continue; + + // opCmp returns an int and opEquals returns a bool + bool isCmp = false, isEq = false; + if( returnTypeId == asTYPEID_INT32 && strcmp(func->GetName(), "opCmp") == 0 ) + isCmp = true; + if( returnTypeId == asTYPEID_BOOL && strcmp(func->GetName(), "opEquals") == 0 ) + isEq = true; + + if( !isCmp && !isEq ) + continue; + + // The parameter must either be a reference to the subtype or a handle to the subtype + int paramTypeId; + func->GetParam(0, ¶mTypeId, &flags); + + if( (paramTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) != (subTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) ) + continue; + + if( (flags & asTM_INREF) ) + { + if( (paramTypeId & asTYPEID_OBJHANDLE) || (mustBeConst && !(flags & asTM_CONST)) ) + continue; + } + else if( paramTypeId & asTYPEID_OBJHANDLE ) + { + if( mustBeConst && !(paramTypeId & asTYPEID_HANDLETOCONST) ) + continue; + } + else + continue; + + if( isCmp ) + { + if( cache->cmpFunc || cache->cmpFuncReturnCode ) + { + cache->cmpFunc = 0; + cache->cmpFuncReturnCode = asMULTIPLE_FUNCTIONS; + } + else + cache->cmpFunc = func; + } + else if( isEq ) + { + if( cache->eqFunc || cache->eqFuncReturnCode ) + { + cache->eqFunc = 0; + cache->eqFuncReturnCode = asMULTIPLE_FUNCTIONS; + } + else + cache->eqFunc = func; + } + } + } + } + + if( cache->eqFunc == 0 && cache->eqFuncReturnCode == 0 ) + cache->eqFuncReturnCode = asNO_FUNCTION; + if( cache->cmpFunc == 0 && cache->cmpFuncReturnCode == 0 ) + cache->cmpFuncReturnCode = asNO_FUNCTION; + + // Set the user data only at the end so others that retrieve it will know it is complete + objType->SetUserData(cache, ARRAY_CACHE); + + asReleaseExclusiveLock(); +} + +// GC behaviour +void CScriptArray::EnumReferences(asIScriptEngine *engine) +{ + // TODO: If garbage collection can be done from a separate thread, then this method must be + // protected so that it doesn't get lost during the iteration if the array is modified + + // If the array is holding handles, then we need to notify the GC of them + if( subTypeId & asTYPEID_MASK_OBJECT ) + { + void **d = (void**)buffer->data; + for( asUINT n = 0; n < buffer->numElements; n++ ) + { + if( d[n] ) + engine->GCEnumCallback(d[n]); + } + } +} + +// GC behaviour +void CScriptArray::ReleaseAllHandles(asIScriptEngine *) +{ + // Resizing to zero will release everything + Resize(0); +} + +void CScriptArray::AddRef() const +{ + // Clear the GC flag then increase the counter + gcFlag = false; + asAtomicInc(refCount); +} + +void CScriptArray::Release() const +{ + // Clearing the GC flag then descrease the counter + gcFlag = false; + if( asAtomicDec(refCount) == 0 ) + { + // When reaching 0 no more references to this instance + // exists and the object should be destroyed + this->~CScriptArray(); + userFree(const_cast(this)); + } +} + +// GC behaviour +int CScriptArray::GetRefCount() +{ + return refCount; +} + +// GC behaviour +void CScriptArray::SetFlag() +{ + gcFlag = true; +} + +// GC behaviour +bool CScriptArray::GetFlag() +{ + return gcFlag; +} + +//-------------------------------------------- +// Generic calling conventions + +static void ScriptArrayFactory_Generic(asIScriptGeneric *gen) +{ + asITypeInfo *ti = *(asITypeInfo**)gen->GetAddressOfArg(0); + + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ti); +} + +static void ScriptArrayFactory2_Generic(asIScriptGeneric *gen) +{ + asITypeInfo *ti = *(asITypeInfo**)gen->GetAddressOfArg(0); + asUINT length = gen->GetArgDWord(1); + + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ti, length); +} + +static void ScriptArrayListFactory_Generic(asIScriptGeneric *gen) +{ + asITypeInfo *ti = *(asITypeInfo**)gen->GetAddressOfArg(0); + void *buf = gen->GetArgAddress(1); + + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ti, buf); +} + +static void ScriptArrayFactoryDefVal_Generic(asIScriptGeneric *gen) +{ + asITypeInfo *ti = *(asITypeInfo**)gen->GetAddressOfArg(0); + asUINT length = gen->GetArgDWord(1); + void *defVal = gen->GetArgAddress(2); + + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ti, length, defVal); +} + +static void ScriptArrayTemplateCallback_Generic(asIScriptGeneric *gen) +{ + asITypeInfo *ti = *(asITypeInfo**)gen->GetAddressOfArg(0); + bool *dontGarbageCollect = *(bool**)gen->GetAddressOfArg(1); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = ScriptArrayTemplateCallback(ti, *dontGarbageCollect); +} + +static void ScriptArrayAssignment_Generic(asIScriptGeneric *gen) +{ + CScriptArray *other = (CScriptArray*)gen->GetArgObject(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *self = *other; + gen->SetReturnObject(self); +} + +static void ScriptArrayEquals_Generic(asIScriptGeneric *gen) +{ + CScriptArray *other = (CScriptArray*)gen->GetArgObject(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + gen->SetReturnByte(self->operator==(*other)); +} + +static void ScriptArrayFind_Generic(asIScriptGeneric *gen) +{ + void *value = gen->GetArgAddress(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + gen->SetReturnDWord(self->Find(value)); +} + +static void ScriptArrayFind2_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + void *value = gen->GetArgAddress(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + gen->SetReturnDWord(self->Find(index, value)); +} + +static void ScriptArrayFindByRef_Generic(asIScriptGeneric *gen) +{ + void *value = gen->GetArgAddress(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + gen->SetReturnDWord(self->FindByRef(value)); +} + +static void ScriptArrayFindByRef2_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + void *value = gen->GetArgAddress(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + gen->SetReturnDWord(self->FindByRef(index, value)); +} + +static void ScriptArrayAt_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + gen->SetReturnAddress(self->At(index)); +} + +static void ScriptArrayInsertAt_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + void *value = gen->GetArgAddress(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->InsertAt(index, value); +} + +static void ScriptArrayInsertAtArray_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + CScriptArray *array = (CScriptArray*)gen->GetArgAddress(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->InsertAt(index, *array); +} + +static void ScriptArrayRemoveAt_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->RemoveAt(index); +} + +static void ScriptArrayRemoveRange_Generic(asIScriptGeneric *gen) +{ + asUINT start = gen->GetArgDWord(0); + asUINT count = gen->GetArgDWord(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->RemoveRange(start, count); +} + +static void ScriptArrayInsertLast_Generic(asIScriptGeneric *gen) +{ + void *value = gen->GetArgAddress(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->InsertLast(value); +} + +static void ScriptArrayRemoveLast_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->RemoveLast(); +} + +static void ScriptArrayLength_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + gen->SetReturnDWord(self->GetSize()); +} + +static void ScriptArrayResize_Generic(asIScriptGeneric *gen) +{ + asUINT size = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + self->Resize(size); +} + +static void ScriptArrayReserve_Generic(asIScriptGeneric *gen) +{ + asUINT size = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->Reserve(size); +} + +static void ScriptArraySortAsc_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SortAsc(); +} + +static void ScriptArrayReverse_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->Reverse(); +} + +static void ScriptArrayIsEmpty_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->IsEmpty(); +} + +static void ScriptArraySortAsc2_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + asUINT count = gen->GetArgDWord(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SortAsc(index, count); +} + +static void ScriptArraySortDesc_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SortDesc(); +} + +static void ScriptArraySortDesc2_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + asUINT count = gen->GetArgDWord(1); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SortDesc(index, count); +} + +static void ScriptArrayAddRef_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptArrayRelease_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->Release(); +} + +static void ScriptArrayGetRefCount_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->GetRefCount(); +} + +static void ScriptArraySetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptArrayGetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->GetFlag(); +} + +static void ScriptArrayEnumReferences_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptArrayReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +static void RegisterScriptArray_Generic(asIScriptEngine *engine) +{ + int r = 0; + UNUSED_VAR(r); + + engine->SetTypeInfoUserDataCleanupCallback(CleanupTypeInfoArrayCache, ARRAY_CACHE); + + r = engine->RegisterObjectType("array", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptArrayTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTION(ScriptArrayFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint length)", asFUNCTION(ScriptArrayFactory2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint length, const T &in value)", asFUNCTION(ScriptArrayFactoryDefVal_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_LIST_FACTORY, "array@ f(int&in, int&in) {repeat T}", asFUNCTION(ScriptArrayListFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptArrayAddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptArrayRelease_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "T &opIndex(uint index)", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "const T &opIndex(uint index) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const T&in value)", asFUNCTION(ScriptArrayInsertAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const array& arr)", asFUNCTION(ScriptArrayInsertAtArray_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void insertLast(const T&in value)", asFUNCTION(ScriptArrayInsertLast_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "void removeAt(uint index)", asFUNCTION(ScriptArrayRemoveAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void removeLast()", asFUNCTION(ScriptArrayRemoveLast_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void removeRange(uint start, uint count)", asFUNCTION(ScriptArrayRemoveRange_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "uint length() const", asFUNCTION(ScriptArrayLength_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void reserve(uint length)", asFUNCTION(ScriptArrayReserve_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void resize(uint length)", asFUNCTION(ScriptArrayResize_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortAsc()", asFUNCTION(ScriptArraySortAsc_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortAsc(uint startAt, uint count)", asFUNCTION(ScriptArraySortAsc2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortDesc()", asFUNCTION(ScriptArraySortDesc_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void sortDesc(uint startAt, uint count)", asFUNCTION(ScriptArraySortDesc2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void reverse()", asFUNCTION(ScriptArrayReverse_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "int find(const T&in if_handle_then_const value) const", asFUNCTION(ScriptArrayFind_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "int find(uint startAt, const T&in if_handle_then_const value) const", asFUNCTION(ScriptArrayFind2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "int findByRef(const T&in if_handle_then_const value) const", asFUNCTION(ScriptArrayFindByRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "int findByRef(uint startAt, const T&in if_handle_then_const value) const", asFUNCTION(ScriptArrayFindByRef2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "bool opEquals(const array&in) const", asFUNCTION(ScriptArrayEquals_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "bool isEmpty() const", asFUNCTION(ScriptArrayIsEmpty_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "uint get_length() const", asFUNCTION(ScriptArrayLength_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void set_length(uint)", asFUNCTION(ScriptArrayResize_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptArrayGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptArraySetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptArrayGetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptArrayEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptArrayReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + +END_AS_NAMESPACE diff --git a/3rdparty/angelscript/src/scriptstdstring.cpp b/3rdparty/angelscript/src/scriptstdstring.cpp new file mode 100644 index 0000000..9426be4 --- /dev/null +++ b/3rdparty/angelscript/src/scriptstdstring.cpp @@ -0,0 +1,1381 @@ +#include "scriptstdstring.h" +#include // assert() +#include // std::stringstream +#include // strstr() +#include // sprintf() +#include // strtod() +#ifndef __psp2__ + #include // setlocale() +#endif + +using namespace std; + +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (void)(x) + +#if AS_USE_STRINGPOOL == 1 + +#ifdef AS_CAN_USE_CPP11 + // The string pool doesn't need to keep a specific order in the + // pool, so the unordered_map is faster than the ordinary map + #include // std::unordered_map +BEGIN_AS_NAMESPACE + typedef unordered_map map_t; +END_AS_NAMESPACE +#else + #include // std::map +BEGIN_AS_NAMESPACE + typedef map map_t; +END_AS_NAMESPACE +#endif + +BEGIN_AS_NAMESPACE + +// By keeping the literal strings in a pool the application +// performance is improved as there are less string copies created. + +// The string pool will be kept as user data in the engine. We'll +// need a specific type to identify the string pool user data. +// We just define a number here that we assume nobody else is using for +// object type user data. The add-ons have reserved the numbers 1000 +// through 1999 for this purpose, so we should be fine. +const asPWORD STRING_POOL = 1001; + +// This global static variable is placed here rather than locally within the +// StringFactory, due to memory leak detectors that don't see the deallocation +// of global variables. By placing the variable globally it will be initialized +// before the memory leak detector starts, thus it won't report the missing +// deallocation. An example of this the Marmalade leak detector initialized with +// IwGxInit() and finished with IwGxTerminate(). +static const string emptyString; + +static const string &StringFactory(asUINT length, const char *s) +{ + // Each engine instance has its own string pool + asIScriptContext *ctx = asGetActiveContext(); + if( ctx == 0 ) + { + // The string factory can only be called from a script + assert( ctx ); + return emptyString; + } + asIScriptEngine *engine = ctx->GetEngine(); + + map_t *pool = reinterpret_cast< map_t* >(engine->GetUserData(STRING_POOL)); + if( !pool ) + { + // The string pool hasn't been created yet, so we'll create it now + asAcquireExclusiveLock(); + + // Make sure the string pool wasn't created while we were waiting for the lock + pool = reinterpret_cast< map_t* >(engine->GetUserData(STRING_POOL)); + if( !pool ) + { + #if defined(__S3E__) + pool = new map_t; + #else + pool = new (nothrow) map_t; + #endif + if( pool == 0 ) + { + ctx->SetException("Out of memory"); + asReleaseExclusiveLock(); + return emptyString; + } + engine->SetUserData(pool, STRING_POOL); + } + + asReleaseExclusiveLock(); + } + + // We can't let other threads modify the pool while we query it + asAcquireSharedLock(); + + // First check if a string object hasn't been created already + map_t::iterator it; + it = pool->find(s); + if( it != pool->end() ) + { + asReleaseSharedLock(); + return it->second; + } + + asReleaseSharedLock(); + + // Acquire an exclusive lock so we can add the new string to the pool + asAcquireExclusiveLock(); + + // Make sure the string wasn't created while we were waiting for the exclusive lock + it = pool->find(s); + if( it == pool->end() ) + { + // Create a new string object + it = pool->insert(map_t::value_type(s, string(s, length))).first; + } + + asReleaseExclusiveLock(); + return it->second; +} + +static void CleanupEngineStringPool(asIScriptEngine *engine) +{ + map_t *pool = reinterpret_cast< map_t* >(engine->GetUserData(STRING_POOL)); + if( pool ) + delete pool; +} + +#else +static string StringFactory(asUINT length, const char *s) +{ + return string(s, length); +} +#endif + +static void ConstructString(string *thisPointer) +{ + new(thisPointer) string(); +} + +static void CopyConstructString(const string &other, string *thisPointer) +{ + new(thisPointer) string(other); +} + +static void DestructString(string *thisPointer) +{ + thisPointer->~string(); +} + +static string &AddAssignStringToString(const string &str, string &dest) +{ + // We don't register the method directly because some compilers + // and standard libraries inline the definition, resulting in the + // linker being unable to find the declaration. + // Example: CLang/LLVM with XCode 4.3 on OSX 10.7 + dest += str; + return dest; +} + +// bool string::isEmpty() +// bool string::empty() // if AS_USE_STLNAMES == 1 +static bool StringIsEmpty(const string &str) +{ + // We don't register the method directly because some compilers + // and standard libraries inline the definition, resulting in the + // linker being unable to find the declaration + // Example: CLang/LLVM with XCode 4.3 on OSX 10.7 + return str.empty(); +} + +static string &AssignUInt64ToString(asQWORD i, string &dest) +{ + ostringstream stream; + stream << i; + dest = stream.str(); + return dest; +} + +static string &AddAssignUInt64ToString(asQWORD i, string &dest) +{ + ostringstream stream; + stream << i; + dest += stream.str(); + return dest; +} + +static string AddStringUInt64(const string &str, asQWORD i) +{ + ostringstream stream; + stream << i; + return str + stream.str(); +} + +static string AddInt64String(asINT64 i, const string &str) +{ + ostringstream stream; + stream << i; + return stream.str() + str; +} + +static string &AssignInt64ToString(asINT64 i, string &dest) +{ + ostringstream stream; + stream << i; + dest = stream.str(); + return dest; +} + +static string &AddAssignInt64ToString(asINT64 i, string &dest) +{ + ostringstream stream; + stream << i; + dest += stream.str(); + return dest; +} + +static string AddStringInt64(const string &str, asINT64 i) +{ + ostringstream stream; + stream << i; + return str + stream.str(); +} + +static string AddUInt64String(asQWORD i, const string &str) +{ + ostringstream stream; + stream << i; + return stream.str() + str; +} + +static string &AssignDoubleToString(double f, string &dest) +{ + ostringstream stream; + stream << f; + dest = stream.str(); + return dest; +} + +static string &AddAssignDoubleToString(double f, string &dest) +{ + ostringstream stream; + stream << f; + dest += stream.str(); + return dest; +} + +static string &AssignFloatToString(float f, string &dest) +{ + ostringstream stream; + stream << f; + dest = stream.str(); + return dest; +} + +static string &AddAssignFloatToString(float f, string &dest) +{ + ostringstream stream; + stream << f; + dest += stream.str(); + return dest; +} + +static string &AssignBoolToString(bool b, string &dest) +{ + ostringstream stream; + stream << (b ? "true" : "false"); + dest = stream.str(); + return dest; +} + +static string &AddAssignBoolToString(bool b, string &dest) +{ + ostringstream stream; + stream << (b ? "true" : "false"); + dest += stream.str(); + return dest; +} + +static string AddStringDouble(const string &str, double f) +{ + ostringstream stream; + stream << f; + return str + stream.str(); +} + +static string AddDoubleString(double f, const string &str) +{ + ostringstream stream; + stream << f; + return stream.str() + str; +} + +static string AddStringFloat(const string &str, float f) +{ + ostringstream stream; + stream << f; + return str + stream.str(); +} + +static string AddFloatString(float f, const string &str) +{ + ostringstream stream; + stream << f; + return stream.str() + str; +} + +static string AddStringBool(const string &str, bool b) +{ + ostringstream stream; + stream << (b ? "true" : "false"); + return str + stream.str(); +} + +static string AddBoolString(bool b, const string &str) +{ + ostringstream stream; + stream << (b ? "true" : "false"); + return stream.str() + str; +} + +static char *StringCharAt(unsigned int i, string &str) +{ + if( i >= str.size() ) + { + // Set a script exception + asIScriptContext *ctx = asGetActiveContext(); + ctx->SetException("Out of range"); + + // Return a null pointer + return 0; + } + + return &str[i]; +} + +// AngelScript signature: +// int string::opCmp(const string &in) const +static int StringCmp(const string &a, const string &b) +{ + int cmp = 0; + if( a < b ) cmp = -1; + else if( a > b ) cmp = 1; + return cmp; +} + +// This function returns the index of the first position where the substring +// exists in the input string. If the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int string::findFirst(const string &in sub, uint start = 0) const +static int StringFindFirst(const string &sub, asUINT start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.find(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// This function returns the index of the first position where the one of the bytes in substring +// exists in the input string. If the characters in the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int string::findFirstOf(const string &in sub, uint start = 0) const +static int StringFindFirstOf(const string &sub, asUINT start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.find_first_of(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// This function returns the index of the last position where the one of the bytes in substring +// exists in the input string. If the characters in the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int string::findLastOf(const string &in sub, uint start = -1) const +static int StringFindLastOf(const string &sub, asUINT start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.find_last_of(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// This function returns the index of the first position where a byte other than those in substring +// exists in the input string. If none is found -1 is returned. +// +// AngelScript signature: +// int string::findFirstNotOf(const string &in sub, uint start = 0) const +static int StringFindFirstNotOf(const string &sub, asUINT start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.find_first_not_of(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// This function returns the index of the last position where a byte other than those in substring +// exists in the input string. If none is found -1 is returned. +// +// AngelScript signature: +// int string::findLastNotOf(const string &in sub, uint start = -1) const +static int StringFindLastNotOf(const string &sub, asUINT start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.find_last_of(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// This function returns the index of the last position where the substring +// exists in the input string. If the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int string::findLast(const string &in sub, int start = -1) const +static int StringFindLast(const string &sub, int start, const string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + return (int)str.rfind(sub, (size_t)(start < 0 ? string::npos : start)); +} + +// AngelScript signature: +// void string::insert(uint pos, const string &in other) +static void StringInsert(unsigned int pos, const string &other, string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + str.insert(pos, other); +} + +// AngelScript signature: +// void string::erase(uint pos, int count = -1) +static void StringErase(unsigned int pos, int count, string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + str.erase(pos, (size_t)(count < 0 ? string::npos : count)); +} + + +// AngelScript signature: +// uint string::length() const +static asUINT StringLength(const string &str) +{ + // We don't register the method directly because the return type changes between 32bit and 64bit platforms + return (asUINT)str.length(); +} + + +// AngelScript signature: +// void string::resize(uint l) +static void StringResize(asUINT l, string &str) +{ + // We don't register the method directly because the argument types change between 32bit and 64bit platforms + str.resize(l); +} + +// AngelScript signature: +// string formatInt(int64 val, const string &in options, uint width) +static string formatInt(asINT64 value, const string &options, asUINT width) +{ + bool leftJustify = options.find("l") != string::npos; + bool padWithZero = options.find("0") != string::npos; + bool alwaysSign = options.find("+") != string::npos; + bool spaceOnSign = options.find(" ") != string::npos; + bool hexSmall = options.find("h") != string::npos; + bool hexLarge = options.find("H") != string::npos; + + string fmt = "%"; + if( leftJustify ) fmt += "-"; + if( alwaysSign ) fmt += "+"; + if( spaceOnSign ) fmt += " "; + if( padWithZero ) fmt += "0"; + +#ifdef _WIN32 + fmt += "*I64"; +#else +#ifdef _LP64 + fmt += "*l"; +#else + fmt += "*ll"; +#endif +#endif + + if( hexSmall ) fmt += "x"; + else if( hexLarge ) fmt += "X"; + else fmt += "d"; + + string buf; + buf.resize(width+30); +#if _MSC_VER >= 1400 && !defined(__S3E__) + // MSVC 8.0 / 2005 or newer + sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, value); +#else + sprintf(&buf[0], fmt.c_str(), width, value); +#endif + buf.resize(strlen(&buf[0])); + + return buf; +} + +// AngelScript signature: +// string formatUInt(uint64 val, const string &in options, uint width) +static string formatUInt(asQWORD value, const string &options, asUINT width) +{ + bool leftJustify = options.find("l") != string::npos; + bool padWithZero = options.find("0") != string::npos; + bool alwaysSign = options.find("+") != string::npos; + bool spaceOnSign = options.find(" ") != string::npos; + bool hexSmall = options.find("h") != string::npos; + bool hexLarge = options.find("H") != string::npos; + + string fmt = "%"; + if( leftJustify ) fmt += "-"; + if( alwaysSign ) fmt += "+"; + if( spaceOnSign ) fmt += " "; + if( padWithZero ) fmt += "0"; + +#ifdef _WIN32 + fmt += "*I64"; +#else +#ifdef _LP64 + fmt += "*l"; +#else + fmt += "*ll"; +#endif +#endif + + if( hexSmall ) fmt += "x"; + else if( hexLarge ) fmt += "X"; + else fmt += "u"; + + string buf; + buf.resize(width+30); +#if _MSC_VER >= 1400 && !defined(__S3E__) + // MSVC 8.0 / 2005 or newer + sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, value); +#else + sprintf(&buf[0], fmt.c_str(), width, value); +#endif + buf.resize(strlen(&buf[0])); + + return buf; +} + +// AngelScript signature: +// string formatFloat(double val, const string &in options, uint width, uint precision) +static string formatFloat(double value, const string &options, asUINT width, asUINT precision) +{ + bool leftJustify = options.find("l") != string::npos; + bool padWithZero = options.find("0") != string::npos; + bool alwaysSign = options.find("+") != string::npos; + bool spaceOnSign = options.find(" ") != string::npos; + bool expSmall = options.find("e") != string::npos; + bool expLarge = options.find("E") != string::npos; + + string fmt = "%"; + if( leftJustify ) fmt += "-"; + if( alwaysSign ) fmt += "+"; + if( spaceOnSign ) fmt += " "; + if( padWithZero ) fmt += "0"; + + fmt += "*.*"; + + if( expSmall ) fmt += "e"; + else if( expLarge ) fmt += "E"; + else fmt += "f"; + + string buf; + buf.resize(width+precision+50); +#if _MSC_VER >= 1400 && !defined(__S3E__) + // MSVC 8.0 / 2005 or newer + sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, precision, value); +#else + sprintf(&buf[0], fmt.c_str(), width, precision, value); +#endif + buf.resize(strlen(&buf[0])); + + return buf; +} + +// AngelScript signature: +// int64 parseInt(const string &in val, uint base = 10, uint &out byteCount = 0) +static asINT64 parseInt(const string &val, asUINT base, asUINT *byteCount) +{ + // Only accept base 10 and 16 + if( base != 10 && base != 16 ) + { + if( byteCount ) *byteCount = 0; + return 0; + } + + const char *end = &val[0]; + + // Determine the sign + bool sign = false; + if( *end == '-' ) + { + sign = true; + end++; + } + else if( *end == '+' ) + end++; + + asINT64 res = 0; + if( base == 10 ) + { + while( *end >= '0' && *end <= '9' ) + { + res *= 10; + res += *end++ - '0'; + } + } + else if( base == 16 ) + { + while( (*end >= '0' && *end <= '9') || + (*end >= 'a' && *end <= 'f') || + (*end >= 'A' && *end <= 'F') ) + { + res *= 16; + if( *end >= '0' && *end <= '9' ) + res += *end++ - '0'; + else if( *end >= 'a' && *end <= 'f' ) + res += *end++ - 'a' + 10; + else if( *end >= 'A' && *end <= 'F' ) + res += *end++ - 'A' + 10; + } + } + + if( byteCount ) + *byteCount = asUINT(size_t(end - val.c_str())); + + if( sign ) + res = -res; + + return res; +} + +// AngelScript signature: +// uint64 parseUInt(const string &in val, uint base = 10, uint &out byteCount = 0) +static asQWORD parseUInt(const string &val, asUINT base, asUINT *byteCount) +{ + // Only accept base 10 and 16 + if (base != 10 && base != 16) + { + if (byteCount) *byteCount = 0; + return 0; + } + + const char *end = &val[0]; + + asQWORD res = 0; + if (base == 10) + { + while (*end >= '0' && *end <= '9') + { + res *= 10; + res += *end++ - '0'; + } + } + else if (base == 16) + { + while ((*end >= '0' && *end <= '9') || + (*end >= 'a' && *end <= 'f') || + (*end >= 'A' && *end <= 'F')) + { + res *= 16; + if (*end >= '0' && *end <= '9') + res += *end++ - '0'; + else if (*end >= 'a' && *end <= 'f') + res += *end++ - 'a' + 10; + else if (*end >= 'A' && *end <= 'F') + res += *end++ - 'A' + 10; + } + } + + if (byteCount) + *byteCount = asUINT(size_t(end - val.c_str())); + + return res; +} + +// AngelScript signature: +// double parseFloat(const string &in val, uint &out byteCount = 0) +double parseFloat(const string &val, asUINT *byteCount) +{ + char *end; + + // WinCE doesn't have setlocale. Some quick testing on my current platform + // still manages to parse the numbers such as "3.14" even if the decimal for the + // locale is ",". +#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) + // Set the locale to C so that we are guaranteed to parse the float value correctly + char *orig = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, "C"); +#endif + + double res = strtod(val.c_str(), &end); + +#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) + // Restore the locale + setlocale(LC_NUMERIC, orig); +#endif + + if( byteCount ) + *byteCount = asUINT(size_t(end - val.c_str())); + + return res; +} + +// This function returns a string containing the substring of the input string +// determined by the starting index and count of characters. +// +// AngelScript signature: +// string string::substr(uint start = 0, int count = -1) const +static string StringSubString(asUINT start, int count, const string &str) +{ + // Check for out-of-bounds + string ret; + if( start < str.length() && count != 0 ) + ret = str.substr(start, (size_t)(count < 0 ? string::npos : count)); + + return ret; +} + +// String equality comparison. +// Returns true iff lhs is equal to rhs. +// +// For some reason gcc 4.7 has difficulties resolving the +// asFUNCTIONPR(operator==, (const string &, const string &) +// makro, so this wrapper was introduced as work around. +static bool StringEquals(const std::string& lhs, const std::string& rhs) +{ + return lhs == rhs; +} + +void RegisterStdString_Native(asIScriptEngine *engine) +{ + int r = 0; + UNUSED_VAR(r); + + // Register the string type +#if AS_CAN_USE_CPP11 + // With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags to use + r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asGetTypeTraits()); assert( r >= 0 ); +#else + r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); +#endif + +#if AS_USE_STRINGPOOL == 1 + // Register the string factory + r = engine->RegisterStringFactory("const string &", asFUNCTION(StringFactory), asCALL_CDECL); assert( r >= 0 ); + + // Register the cleanup callback for the string pool + engine->SetEngineUserDataCleanupCallback(CleanupEngineStringPool, STRING_POOL); +#else + // Register the string factory + r = engine->RegisterStringFactory("string", asFUNCTION(StringFactory), asCALL_CDECL); assert( r >= 0 ); +#endif + + // Register the object operator overloads + r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(CopyConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asMETHODPR(string, operator =, (const string&), string&), asCALL_THISCALL); assert( r >= 0 ); + // Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails + r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); +// r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asMETHODPR(string, operator+=, (const string&), string&), asCALL_THISCALL); assert( r >= 0 ); + + // Need to use a wrapper for operator== otherwise gcc 4.7 fails to compile + r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTIONPR(StringEquals, (const string &, const string &), bool), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTIONPR(operator +, (const string &, const string &), string), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + + // The string length can be accessed through methods or through virtual property + r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "uint get_length() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void set_length(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + // Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails +// r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asMETHOD(string, empty), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asFUNCTION(StringIsEmpty), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + // Register the index operator, both as a mutator and as an inspector + // Note that we don't register the operator[] directly, as it doesn't do bounds checking + r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + // Automatic conversion from values + r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddStringInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddStringUInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddStringBool), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBoolString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + // Utilities + r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int findFirstOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstOf), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findFirstNotOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstNotOf), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int findLastOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastOf), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase), asCALL_CDECL_OBJLAST); assert(r >= 0); + + + r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("int64 parseInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseInt), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("uint64 parseUInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseUInt), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("double parseFloat(const string &in, uint &out byteCount = 0)", asFUNCTION(parseFloat), asCALL_CDECL); assert(r >= 0); + +#if AS_USE_STLNAMES == 1 + // Same as length + r = engine->RegisterObjectMethod("string", "uint size() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + // Same as isEmpty + r = engine->RegisterObjectMethod("string", "bool empty() const", asFUNCTION(StringIsEmpty), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + // Same as findFirst + r = engine->RegisterObjectMethod("string", "int find(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + // Same as findLast + r = engine->RegisterObjectMethod("string", "int rfind(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); assert( r >= 0 ); +#endif + + // TODO: Implement the following + // findAndReplace - replaces a text found in the string + // replaceRange - replaces a range of bytes in the string + // multiply/times/opMul/opMul_r - takes the string and multiplies it n times, e.g. "-".multiply(5) returns "-----" +} + +#if AS_USE_STRINGPOOL == 1 +static void StringFactoryGeneric(asIScriptGeneric *gen) +{ + asUINT length = gen->GetArgDWord(0); + const char *s = (const char*)gen->GetArgAddress(1); + + // Return a reference to a string + gen->SetReturnAddress(const_cast(&StringFactory(length, s))); +} +#else +static void StringFactoryGeneric(asIScriptGeneric *gen) +{ + asUINT length = gen->GetArgDWord(0); + const char *s = (const char*)gen->GetArgAddress(1); + + // Return a string value + new (gen->GetAddressOfReturnLocation()) string(StringFactory(length, s)); +} +#endif + +static void ConstructStringGeneric(asIScriptGeneric * gen) +{ + new (gen->GetObject()) string(); +} + +static void CopyConstructStringGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetArgObject(0)); + new (gen->GetObject()) string(*a); +} + +static void DestructStringGeneric(asIScriptGeneric * gen) +{ + string * ptr = static_cast(gen->GetObject()); + ptr->~string(); +} + +static void AssignStringGeneric(asIScriptGeneric *gen) +{ + string * a = static_cast(gen->GetArgObject(0)); + string * self = static_cast(gen->GetObject()); + *self = *a; + gen->SetReturnAddress(self); +} + +static void AddAssignStringGeneric(asIScriptGeneric *gen) +{ + string * a = static_cast(gen->GetArgObject(0)); + string * self = static_cast(gen->GetObject()); + *self += *a; + gen->SetReturnAddress(self); +} + +static void StringEqualsGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + string * b = static_cast(gen->GetArgAddress(0)); + *(bool*)gen->GetAddressOfReturnLocation() = (*a == *b); +} + +static void StringCmpGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + string * b = static_cast(gen->GetArgAddress(0)); + + int cmp = 0; + if( *a < *b ) cmp = -1; + else if( *a > *b ) cmp = 1; + + *(int*)gen->GetAddressOfReturnLocation() = cmp; +} + +static void StringAddGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + string * b = static_cast(gen->GetArgAddress(0)); + string ret_val = *a + *b; + gen->SetReturnObject(&ret_val); +} + +static void StringLengthGeneric(asIScriptGeneric * gen) +{ + string * self = static_cast(gen->GetObject()); + *static_cast(gen->GetAddressOfReturnLocation()) = (asUINT)self->length(); +} + +static void StringIsEmptyGeneric(asIScriptGeneric * gen) +{ + string * self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringIsEmpty(*self); +} + +static void StringResizeGeneric(asIScriptGeneric * gen) +{ + string * self = static_cast(gen->GetObject()); + self->resize(*static_cast(gen->GetAddressOfArg(0))); +} + +static void StringInsert_Generic(asIScriptGeneric *gen) +{ + string * self = static_cast(gen->GetObject()); + asUINT pos = gen->GetArgDWord(0); + string *other = reinterpret_cast(gen->GetArgAddress(1)); + StringInsert(pos, *other, *self); +} + +static void StringErase_Generic(asIScriptGeneric *gen) +{ + string * self = static_cast(gen->GetObject()); + asUINT pos = gen->GetArgDWord(0); + int count = int(gen->GetArgDWord(1)); + StringErase(pos, count, *self); +} + +static void StringFindFirst_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindFirst(*find, start, *self); +} + +static void StringFindLast_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindLast(*find, start, *self); +} + +static void StringFindFirstOf_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindFirstOf(*find, start, *self); +} + +static void StringFindLastOf_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindLastOf(*find, start, *self); +} + +static void StringFindFirstNotOf_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindFirstNotOf(*find, start, *self); +} + +static void StringFindLastNotOf_Generic(asIScriptGeneric * gen) +{ + string *find = reinterpret_cast(gen->GetArgAddress(0)); + asUINT start = gen->GetArgDWord(1); + string *self = reinterpret_cast(gen->GetObject()); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = StringFindLastNotOf(*find, start, *self); +} + +static void formatInt_Generic(asIScriptGeneric * gen) +{ + asINT64 val = gen->GetArgQWord(0); + string *options = reinterpret_cast(gen->GetArgAddress(1)); + asUINT width = gen->GetArgDWord(2); + new(gen->GetAddressOfReturnLocation()) string(formatInt(val, *options, width)); +} + +static void formatUInt_Generic(asIScriptGeneric * gen) +{ + asQWORD val = gen->GetArgQWord(0); + string *options = reinterpret_cast(gen->GetArgAddress(1)); + asUINT width = gen->GetArgDWord(2); + new(gen->GetAddressOfReturnLocation()) string(formatUInt(val, *options, width)); +} + +static void formatFloat_Generic(asIScriptGeneric *gen) +{ + double val = gen->GetArgDouble(0); + string *options = reinterpret_cast(gen->GetArgAddress(1)); + asUINT width = gen->GetArgDWord(2); + asUINT precision = gen->GetArgDWord(3); + new(gen->GetAddressOfReturnLocation()) string(formatFloat(val, *options, width, precision)); +} + +static void parseInt_Generic(asIScriptGeneric *gen) +{ + string *str = reinterpret_cast(gen->GetArgAddress(0)); + asUINT base = gen->GetArgDWord(1); + asUINT *byteCount = reinterpret_cast(gen->GetArgAddress(2)); + gen->SetReturnQWord(parseInt(*str,base,byteCount)); +} + +static void parseUInt_Generic(asIScriptGeneric *gen) +{ + string *str = reinterpret_cast(gen->GetArgAddress(0)); + asUINT base = gen->GetArgDWord(1); + asUINT *byteCount = reinterpret_cast(gen->GetArgAddress(2)); + gen->SetReturnQWord(parseUInt(*str, base, byteCount)); +} + +static void parseFloat_Generic(asIScriptGeneric *gen) +{ + string *str = reinterpret_cast(gen->GetArgAddress(0)); + asUINT *byteCount = reinterpret_cast(gen->GetArgAddress(1)); + gen->SetReturnDouble(parseFloat(*str,byteCount)); +} + +static void StringCharAtGeneric(asIScriptGeneric * gen) +{ + unsigned int index = gen->GetArgDWord(0); + string * self = static_cast(gen->GetObject()); + + if (index >= self->size()) + { + // Set a script exception + asIScriptContext *ctx = asGetActiveContext(); + ctx->SetException("Out of range"); + + gen->SetReturnAddress(0); + } + else + { + gen->SetReturnAddress(&(self->operator [](index))); + } +} + +static void AssignInt2StringGeneric(asIScriptGeneric *gen) +{ + asINT64 *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self = sstr.str(); + gen->SetReturnAddress(self); +} + +static void AssignUInt2StringGeneric(asIScriptGeneric *gen) +{ + asQWORD *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self = sstr.str(); + gen->SetReturnAddress(self); +} + +static void AssignDouble2StringGeneric(asIScriptGeneric *gen) +{ + double *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self = sstr.str(); + gen->SetReturnAddress(self); +} + +static void AssignFloat2StringGeneric(asIScriptGeneric *gen) +{ + float *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self = sstr.str(); + gen->SetReturnAddress(self); +} + +static void AssignBool2StringGeneric(asIScriptGeneric *gen) +{ + bool *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << (*a ? "true" : "false"); + *self = sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddAssignDouble2StringGeneric(asIScriptGeneric * gen) +{ + double * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self += sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddAssignFloat2StringGeneric(asIScriptGeneric * gen) +{ + float * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self += sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddAssignInt2StringGeneric(asIScriptGeneric * gen) +{ + asINT64 * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self += sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddAssignUInt2StringGeneric(asIScriptGeneric * gen) +{ + asQWORD * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self += sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddAssignBool2StringGeneric(asIScriptGeneric * gen) +{ + bool * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << (*a ? "true" : "false"); + *self += sstr.str(); + gen->SetReturnAddress(self); +} + +static void AddString2DoubleGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + double * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddString2FloatGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + float * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddString2IntGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + asINT64 * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddString2UIntGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + asQWORD * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddString2BoolGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + bool * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << (*b ? "true" : "false"); + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddDouble2StringGeneric(asIScriptGeneric * gen) +{ + double* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddFloat2StringGeneric(asIScriptGeneric * gen) +{ + float* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddInt2StringGeneric(asIScriptGeneric * gen) +{ + asINT64* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddUInt2StringGeneric(asIScriptGeneric * gen) +{ + asQWORD* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void AddBool2StringGeneric(asIScriptGeneric * gen) +{ + bool* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << (*a ? "true" : "false") << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + +static void StringSubString_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + string *str = (string*)gen->GetObject(); + asUINT start = *(int*)gen->GetAddressOfArg(0); + int count = *(int*)gen->GetAddressOfArg(1); + + // Return the substring + new(gen->GetAddressOfReturnLocation()) string(StringSubString(start, count, *str)); +} + +void RegisterStdString_Generic(asIScriptEngine *engine) +{ + int r = 0; + UNUSED_VAR(r); + + // Register the string type + r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); + +#if AS_USE_STRINGPOOL == 1 + // Register the string factory + r = engine->RegisterStringFactory("const string &", asFUNCTION(StringFactoryGeneric), asCALL_GENERIC); assert( r >= 0 ); + + // Register the cleanup callback for the string pool + engine->SetEngineUserDataCleanupCallback(CleanupEngineStringPool, STRING_POOL); +#else + // Register the string factory + r = engine->RegisterStringFactory("string", asFUNCTION(StringFactoryGeneric), asCALL_GENERIC); assert( r >= 0 ); +#endif + + // Register the object operator overloads + r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(CopyConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructStringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asFUNCTION(AssignStringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringEqualsGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmpGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTION(StringAddGeneric), asCALL_GENERIC); assert( r >= 0 ); + + // Register the object methods + r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLengthGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResizeGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "uint get_length() const", asFUNCTION(StringLengthGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void set_length(uint)", asFUNCTION(StringResizeGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asFUNCTION(StringIsEmptyGeneric), asCALL_GENERIC); assert( r >= 0 ); + + // Register the index operator, both as a mutator and as an inspector + r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 ); + + // Automatic conversion from values + r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddString2DoubleGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddString2FloatGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddString2IntGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddString2UIntGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddString2BoolGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findFirstOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findFirstNotOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstNotOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findLastOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase_Generic), asCALL_GENERIC); assert(r >= 0); + + + r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int64 parseInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseInt_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("uint64 parseUInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseUInt_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("double parseFloat(const string &in, uint &out byteCount = 0)", asFUNCTION(parseFloat_Generic), asCALL_GENERIC); assert(r >= 0); +} + +void RegisterStdString(asIScriptEngine * engine) +{ + if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) + RegisterStdString_Generic(engine); + else + RegisterStdString_Native(engine); +} + +END_AS_NAMESPACE + + + + diff --git a/3rdparty/angelscript/src/scriptstdstring_utils.cpp b/3rdparty/angelscript/src/scriptstdstring_utils.cpp new file mode 100644 index 0000000..f1ddbe9 --- /dev/null +++ b/3rdparty/angelscript/src/scriptstdstring_utils.cpp @@ -0,0 +1,129 @@ +#include +#include "scriptstdstring.h" +#include "scriptarray.h" +#include +#include + +using namespace std; + +BEGIN_AS_NAMESPACE + +// This function takes an input string and splits it into parts by looking +// for a specified delimiter. Example: +// +// string str = "A|B||D"; +// array@ array = str.split("|"); +// +// The resulting array has the following elements: +// +// {"A", "B", "", "D"} +// +// AngelScript signature: +// array@ string::split(const string &in delim) const +static CScriptArray *StringSplit(const string &delim, const string &str) +{ + // Obtain a pointer to the engine + asIScriptContext *ctx = asGetActiveContext(); + asIScriptEngine *engine = ctx->GetEngine(); + + // TODO: This should only be done once + // TODO: This assumes that CScriptArray was already registered + asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array"); + + // Create the array object + CScriptArray *array = CScriptArray::Create(arrayType); + + // Find the existence of the delimiter in the input string + int pos = 0, prev = 0, count = 0; + while( (pos = (int)str.find(delim, prev)) != (int)string::npos ) + { + // Add the part to the array + array->Resize(array->GetSize()+1); + ((string*)array->At(count))->assign(&str[prev], pos-prev); + + // Find the next part + count++; + prev = pos + (int)delim.length(); + } + + // Add the remaining part + array->Resize(array->GetSize()+1); + ((string*)array->At(count))->assign(&str[prev]); + + return array; +} + +static void StringSplit_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + string *str = (string*)gen->GetObject(); + string *delim = *(string**)gen->GetAddressOfArg(0); + + // Return the array by handle + *(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str); +} + + + +// This function takes as input an array of string handles as well as a +// delimiter and concatenates the array elements into one delimited string. +// Example: +// +// array array = {"A", "B", "", "D"}; +// string str = join(array, "|"); +// +// The resulting string is: +// +// "A|B||D" +// +// AngelScript signature: +// string join(const array &in array, const string &in delim) +static string StringJoin(const CScriptArray &array, const string &delim) +{ + // Create the new string + string str = ""; + if( array.GetSize() ) + { + int n; + for( n = 0; n < (int)array.GetSize() - 1; n++ ) + { + str += *(string*)array.At(n); + str += delim; + } + + // Add the last part + str += *(string*)array.At(n); + } + + return str; +} + +static void StringJoin_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0); + string *delim = *(string**)gen->GetAddressOfArg(1); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim)); +} + +// This is where the utility functions are registered. +// The string type must have been registered first. +void RegisterStdStringUtils(asIScriptEngine *engine) +{ + int r; + + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); + } + else + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); + } +} + +END_AS_NAMESPACE diff --git a/3rdparty/imgui/CMakeLists.txt b/3rdparty/imgui/CMakeLists.txt new file mode 100644 index 0000000..ce78b83 --- /dev/null +++ b/3rdparty/imgui/CMakeLists.txt @@ -0,0 +1,6 @@ + project(ImGui) + +add_library(ImGui src/imgui.cpp src/imgui_draw.cpp) + +include_directories(include) +include_directories(SYSTEM ${LIBRARY_STB_INCLUDE_DIR}) diff --git a/3rdparty/imgui/LICENSE b/3rdparty/imgui/LICENSE new file mode 100644 index 0000000..a01aeb2 --- /dev/null +++ b/3rdparty/imgui/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2015 Omar Cornut and ImGui contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/3rdparty/imgui/include/imconfig.h b/3rdparty/imgui/include/imconfig.h new file mode 100644 index 0000000..84dc221 --- /dev/null +++ b/3rdparty/imgui/include/imconfig.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// USER IMPLEMENTATION +// This file contains compile-time options for ImGui. +// Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). +//----------------------------------------------------------------------------- + +#pragma once + +//---- Define assertion handler. Defaults to calling assert(). +//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) + +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. +//#define IMGUI_API __declspec( dllexport ) +//#define IMGUI_API __declspec( dllimport ) + +//---- Include imgui_user.h at the end of imgui.h +//#define IMGUI_INCLUDE_IMGUI_USER_H + +//---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS + +//---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty) +//#define IMGUI_DISABLE_TEST_WINDOWS + +//---- Don't define obsolete functions names +//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +//---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) +//#define IMGUI_USE_BGRA_PACKED_COLOR + +//---- Implement STB libraries in a namespace to avoid conflicts +//#define IMGUI_STB_NAMESPACE ImGuiStb + +//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. +/* +#define IM_VEC2_CLASS_EXTRA \ + ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ + operator MyVec2() const { return MyVec2(x,y); } + +#define IM_VEC4_CLASS_EXTRA \ + ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ + operator MyVec4() const { return MyVec4(x,y,z,w); } +*/ + +//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. +//---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. +/* +namespace ImGui +{ + void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); +} +*/ + + diff --git a/3rdparty/imgui/include/imgui.h b/3rdparty/imgui/include/imgui.h new file mode 100644 index 0000000..0a019ca --- /dev/null +++ b/3rdparty/imgui/include/imgui.h @@ -0,0 +1,1400 @@ + // dear imgui, v1.50 WIP +// (headers) + +// See imgui.cpp file for documentation. +// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code. +// Read 'Programmer guide' in imgui.cpp for notes on how to setup ImGui in your codebase. +// Get latest version at https://github.com/ocornut/imgui + +#pragma once + +#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H) +#include "imconfig.h" // User-editable configuration file +#endif +#include // FLT_MAX +#include // va_list +#include // ptrdiff_t, NULL +#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp + +#define IMGUI_VERSION "1.50 WIP" + +// Define attributes of all API symbols declarations, e.g. for DLL under Windows. +#ifndef IMGUI_API +#define IMGUI_API +#endif + +// Define assertion handler. +#ifndef IM_ASSERT +#include +#define IM_ASSERT(_EXPR) assert(_EXPR) +#endif + +// Some compilers support applying printf-style warnings to user functions. +#if defined(__clang__) || defined(__GNUC__) +#define IM_PRINTFARGS(FMT) __attribute__((format(printf, FMT, (FMT+1)))) +#else +#define IM_PRINTFARGS(FMT) +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + +// Forward declarations +struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() +struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) +struct ImDrawData; // All draw command lists required to render the frame +struct ImDrawList; // A single draw command list (generally one per window) +struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) +struct ImFont; // Runtime data for a single font within a parent ImFontAtlas +struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF font loader +struct ImFontConfig; // Configuration data when adding a font or merging fonts +struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 +struct ImGuiIO; // Main configuration and I/O between your application and ImGui +struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiStorage; // Simple custom key value storage +struct ImGuiStyle; // Runtime data for styling/colors +struct ImGuiTextFilter; // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +struct ImGuiTextBuffer; // Text buffer for logging/accumulating text +struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) +struct ImGuiSizeConstraintCallbackData;// Structure used to constraint window size in custom ways when using custom ImGuiSizeConstraintCallback (rare/advanced use) +struct ImGuiListClipper; // Helper to manually clip large list of items +struct ImGuiContext; // ImGui context (opaque) + +// Typedefs and Enumerations (declared as int for compatibility and to not pollute the top of this file) +typedef unsigned int ImU32; // 32-bit unsigned integer (typically used to store packed colors) +typedef unsigned int ImGuiID; // unique ID used by widgets (typically hashed from a stack of string) +typedef unsigned short ImWchar; // character for keyboard input/display +typedef void* ImTextureID; // user data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) +typedef int ImGuiCol; // a color identifier for styling // enum ImGuiCol_ +typedef int ImGuiStyleVar; // a variable identifier for styling // enum ImGuiStyleVar_ +typedef int ImGuiKey; // a key identifier (ImGui-side enum) // enum ImGuiKey_ +typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_ +typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_ +typedef int ImGuiWindowFlags; // window flags for Begin*() // enum ImGuiWindowFlags_ +typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_ +typedef int ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_ +typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_ +typedef int ImGuiTreeNodeFlags; // flags for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_ +typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); +typedef void (*ImGuiSizeConstraintCallback)(ImGuiSizeConstraintCallbackData* data); + +// Others helpers at bottom of the file: +// class ImVector<> // Lightweight std::vector like class. +// IMGUI_ONCE_UPON_A_FRAME // Execute a block of code once per frame only (convenient for creating UI within deep-nested code that runs multiple times) + +struct ImVec2 +{ + float x, y; + ImVec2() { x = y = 0.0f; } + ImVec2(float _x, float _y) { x = _x; y = _y; } +#ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2. + IM_VEC2_CLASS_EXTRA +#endif +}; + +struct ImVec4 +{ + float x, y, z, w; + ImVec4() { x = y = z = w = 0.0f; } + ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } +#ifdef IM_VEC4_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec4. + IM_VEC4_CLASS_EXTRA +#endif +}; + +// ImGui end-user API +// In a namespace so that user can add extra functions in a separate file (e.g. Value() helpers for your vector or common types) +namespace ImGui +{ + // Main + IMGUI_API ImGuiIO& GetIO(); + IMGUI_API ImGuiStyle& GetStyle(); + IMGUI_API ImDrawData* GetDrawData(); // same value as passed to your io.RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame() + IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until NewFrame()/Render(). + IMGUI_API void Render(); // ends the ImGui frame, finalize rendering data, then call your io.RenderDrawListsFn() function if set. + IMGUI_API void Shutdown(); + IMGUI_API void ShowUserGuide(); // help block + IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // style editor block. you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) + IMGUI_API void ShowTestWindow(bool* p_open = NULL); // test window demonstrating ImGui features + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // metrics window for debugging ImGui + + // Window + IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // push window to the stack and start appending to it. see .cpp for details. return false when window is collapsed, so you can early out in your code. 'bool* p_open' creates a widget on the upper-right to close the window (which sets your bool to false). + IMGUI_API bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // OBSOLETE. this is the older/longer API. the extra parameters aren't very relevant. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! might obsolete this API eventually. + IMGUI_API void End(); // finish appending to current window, pop it off the window stack. + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " + IMGUI_API void EndChild(); + IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates + IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() + IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates + IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList api) + IMGUI_API ImVec2 GetWindowSize(); // get current window size + IMGUI_API float GetWindowWidth(); + IMGUI_API float GetWindowHeight(); + IMGUI_API bool IsWindowCollapsed(); + IMGUI_API void SetWindowFontScale(float scale); // per-window font scale. Adjust IO.FontGlobalScale if you want to scale all windows + + IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position. call before Begin() + IMGUI_API void SetNextWindowPosCenter(ImGuiSetCond cond = 0); // set next window position to be centered on screen. call before Begin() + IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() + IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. + IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (enforce the range of scrollbars). set axis to 0.0f to leave it automatic. call before Begin() + IMGUI_API void SetNextWindowContentWidth(float width); // set next window content width (enforce the range of horizontal scrollbar). call before Begin() + IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set next window collapsed state. call before Begin() + IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() + IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. + IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. + IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). + IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / front-most. prefer using SetNextWindowFocus(). + IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond = 0); // set named window position. + IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. + IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond = 0); // set named window collapsed state + IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. + + IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] + IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] + IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X + IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y + IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] + IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] + IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. + IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. + IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use negative 'offset' to access previous widgets. + IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) + IMGUI_API ImGuiStorage* GetStateStorage(); + + // Parameters stacks (shared) + IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font + IMGUI_API void PopFont(); + IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); + IMGUI_API void PopStyleColor(int count = 1); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); + IMGUI_API void PopStyleVar(int count = 1); + IMGUI_API ImFont* GetFont(); // get current font + IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied + IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API + IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier + IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied + + // Parameters stacks (current window) + IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) + IMGUI_API void PopItemWidth(); + IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position + IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space + IMGUI_API void PopTextWrapPos(); + IMGUI_API void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PopAllowKeyboardFocus(); + IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (uses io.KeyRepeatDelay/io.KeyRepeatRate for now). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PopButtonRepeat(); + + // Cursor / Layout + IMGUI_API void Separator(); // horizontal line + IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally + IMGUI_API void NewLine(); // undo a SameLine() + IMGUI_API void Spacing(); // add vertical spacing + IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size + IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if >0 + IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if >0 + IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) + IMGUI_API void EndGroup(); + IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position + IMGUI_API float GetCursorPosX(); // " + IMGUI_API float GetCursorPosY(); // " + IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " + IMGUI_API void SetCursorPosX(float x); // " + IMGUI_API void SetCursorPosY(float y); // " + IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void AlignFirstTextHeightToWidgets(); // call once if the first item on the line is a Text() item and you want to vertically lower it to match subsequent (bigger) widgets + IMGUI_API float GetTextLineHeight(); // height of font == GetWindowFontSize() + IMGUI_API float GetTextLineHeightWithSpacing(); // distance (in pixels) between 2 consecutive lines of text == GetWindowFontSize() + GetStyle().ItemSpacing.y + IMGUI_API float GetItemsLineHeightWithSpacing(); // distance (in pixels) between 2 consecutive lines of standard height widgets == GetWindowFontSize() + GetStyle().FramePadding.y*2 + GetStyle().ItemSpacing.y + + // Columns + // You can also use SameLine(pos_x) for simplified columning. The columns API is still work-in-progress and rather lacking. + IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); // setup number of columns. use an identifier to distinguish multiple column sets. close with Columns(1). + IMGUI_API void NextColumn(); // next column + IMGUI_API int GetColumnIndex(); // get current column index + IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetcolumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this + IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column + IMGUI_API float GetColumnWidth(int column_index = -1); // column width (== GetColumnOffset(GetColumnIndex()+1) - GetColumnOffset(GetColumnOffset()) + IMGUI_API int GetColumnsCount(); // number of columns (what was passed to Columns()) + + // ID scopes + // If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them. + // You can also use the "##foobar" syntax within widget label to distinguish them from each others. Read "A primer on the use of labels/IDs" in the FAQ for more details. + IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the *entire* stack! + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); + IMGUI_API void PushID(const void* ptr_id); + IMGUI_API void PushID(int int_id); + IMGUI_API void PopID(); + IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). useful if you want to query into ImGuiStorage yourself. otherwise rarely needed + IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); + IMGUI_API ImGuiID GetID(const void* ptr_id); + + // Widgets + IMGUI_API void Text(const char* fmt, ...) IM_PRINTFARGS(1); + IMGUI_API void TextV(const char* fmt, va_list args); + IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_PRINTFARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args); + IMGUI_API void TextDisabled(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextDisabledV(const char* fmt, va_list args); + IMGUI_API void TextWrapped(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). + IMGUI_API void TextWrappedV(const char* fmt, va_list args); + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text + IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_PRINTFARGS(2); // display text+label aligned the same way as value+label widgets + IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); + IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses + IMGUI_API void BulletText(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for Bullet()+Text() + IMGUI_API void BulletTextV(const char* fmt, va_list args); + IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button + IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding + IMGUI_API bool Checkbox(const char* label, bool* v); + IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); + IMGUI_API bool RadioButton(const char* label, bool active); + IMGUI_API bool RadioButton(const char* label, int* v, int v_button); + IMGUI_API bool Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0 + IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true); + IMGUI_API bool ColorEdit3(const char* label, float col[3]); // Hint: 'float col[3]' function argument is same as 'float* col'. You can pass address of first element out of a contiguous set, e.g. &myvector.x + IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); // " + IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); // FIXME-OBSOLETE: This is inconsistent with most of the API and will be obsoleted/replaced. + IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); + IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); + IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); + + // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) + // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, remember than a 'float v[3]' function argument is the same as 'float* v'. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound + IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f); + IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound + IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL); + + // Widgets: Input with Keyboard + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); + + // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) + IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders + IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); + IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + + // Widgets: Trees + IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_PRINTFARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_PRINTFARGS(2); // " + IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // " + IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // " + IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); + IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_PRINTFARGS(3); + IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_PRINTFARGS(3); + IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args); + IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args); + IMGUI_API void TreePush(const char* str_id = NULL); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call Push/Pop yourself for layout purpose + IMGUI_API void TreePush(const void* ptr_id = NULL); // " + IMGUI_API void TreePop(); // ~ Unindent()+PopId() + IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() + IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode + IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond = 0); // set next TreeNode/CollapsingHeader open state. + IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). + IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header + + // Widgets: Selectable / Lists + IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height + IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); + IMGUI_API bool ListBox(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. + IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " + IMGUI_API void ListBoxFooter(); // terminate the scrolling region + + // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) + IMGUI_API void Value(const char* prefix, bool b); + IMGUI_API void Value(const char* prefix, int v); + IMGUI_API void Value(const char* prefix, unsigned int v); + IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); + IMGUI_API void ValueColor(const char* prefix, const ImVec4& v); + IMGUI_API void ValueColor(const char* prefix, ImU32 v); + + // Tooltips + IMGUI_API void SetTooltip(const char* fmt, ...) IM_PRINTFARGS(1); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins + IMGUI_API void SetTooltipV(const char* fmt, va_list args); + IMGUI_API void BeginTooltip(); // use to create full-featured tooltip windows that aren't just text + IMGUI_API void EndTooltip(); + + // Menus + IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. only call EndMainMenuBar() if this returns true! + IMGUI_API void EndMainMenuBar(); + IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set). only call EndMenuBar() if this returns true! + IMGUI_API void EndMenuBar(); + IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! + IMGUI_API void EndMenu(); + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment + IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL + + // Popups + IMGUI_API void OpenPopup(const char* str_id); // mark popup as open. popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). + IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! + IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (can't close them by clicking outside) + IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! + IMGUI_API bool BeginPopupContextWindow(bool also_over_items = true, const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on current window. + IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window). + IMGUI_API void EndPopup(); + IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. + + // Logging: all text output from interface is redirected to tty/file/clipboard. By default, tree nodes are automatically opened during logging. + IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty + IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + IMGUI_API void LogFinish(); // stop logging (close file, etc.) + IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard + IMGUI_API void LogText(const char* fmt, ...) IM_PRINTFARGS(1); // pass text data straight to log (without being displayed) + + // Clipping + IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); + IMGUI_API void PopClipRect(); + + // Utilities + IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? + IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this + IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsItemClicked(int mouse_button = 0); // was the last item clicked? (e.g. button/node just clicked on) + IMGUI_API bool IsItemVisible(); // was the last item visible? (aka not out of sight due to clipping/scrolling.) + IMGUI_API bool IsAnyItemHovered(); + IMGUI_API bool IsAnyItemActive(); + IMGUI_API ImVec2 GetItemRectMin(); // get bounding rect of last item in screen space + IMGUI_API ImVec2 GetItemRectMax(); // " + IMGUI_API ImVec2 GetItemRectSize(); // " + IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) + IMGUI_API bool IsWindowFocused(); // is current window focused + IMGUI_API bool IsRootWindowFocused(); // is current root window focused (root = top-most parent of a child, otherwise self) + IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused + IMGUI_API bool IsRootWindowOrAnyChildHovered(); // is current root window or any of its child (including current window) hovered and hoverable (not blocked by a popup) + IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. + IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. + IMGUI_API bool IsPosHoveringAnyWindow(const ImVec2& pos); // is given position hovering any active imgui window + IMGUI_API float GetTime(); + IMGUI_API int GetFrameCount(); + IMGUI_API const char* GetStyleColName(ImGuiCol idx); + IMGUI_API ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = +0.0f); // utility to find the closest point the last item bounding rectangle edge. useful to visually link items + IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. + + IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame + IMGUI_API void EndChildFrame(); + + IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); + IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); + IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); + IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); + + // Inputs + IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] + IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[] array, imgui doesn't know the semantic of each entry, uses your own indices! + IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // uses user's key indices as stored in the keys_down[] array. if repeat=true. uses io.KeyRepeatDelay / KeyRepeatRate + IMGUI_API bool IsKeyReleased(int key_index); // " + IMGUI_API bool IsMouseDown(int button); // is mouse button held + IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) + IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. + IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) + IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window). disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) + IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any visible window + IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. + IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls + IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into + IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API void ResetMouseDragDelta(int button = 0); // + IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you + IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type + IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application handle). e.g. force capture keyboard when your widget is being hovered. + IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application handle). + + // Helpers functions to access functions pointers in ImGui::GetIO() + IMGUI_API void* MemAlloc(size_t sz); + IMGUI_API void MemFree(void* ptr); + IMGUI_API const char* GetClipboardText(); + IMGUI_API void SetClipboardText(const char* text); + + // Internal context access - if you want to use multiple context, share context between modules (e.g. DLL). There is a default context created and active by default. + // All contexts share a same ImFontAtlas by default. If you want different font atlas, you can new() them and overwrite the GetIO().Fonts variable of an ImGui context. + IMGUI_API const char* GetVersion(); + IMGUI_API ImGuiContext* CreateContext(void* (*malloc_fn)(size_t) = NULL, void (*free_fn)(void*) = NULL); + IMGUI_API void DestroyContext(ImGuiContext* ctx); + IMGUI_API ImGuiContext* GetCurrentContext(); + IMGUI_API void SetCurrentContext(ImGuiContext* ctx); + + // Obsolete (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1<<5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+ + static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+ + static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+ + static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETE 1.42+ + static inline bool GetWindowCollapsed() { return ImGui::IsWindowCollapsed(); } // OBSOLETE 1.39+ + static inline bool IsRectClipped(const ImVec2& size) { return !IsRectVisible(size); } // OBSOLETE 1.39+ +#endif + +} // namespace ImGui + +// Flags for ImGui::Begin() +enum ImGuiWindowFlags_ +{ + // Default: 0 + ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar + ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip + ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame + ImGuiWindowFlags_ShowBorders = 1 << 7, // Show borders around windows and items + ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file + ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs + ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar + ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. + ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) + ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) + ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) + // [Internal] + ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitY = 1 << 22, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ComboBox = 1 << 23, // Don't use! For internal use by ComboBox() + ImGuiWindowFlags_Tooltip = 1 << 24, // Don't use! For internal use by BeginTooltip() + ImGuiWindowFlags_Popup = 1 << 25, // Don't use! For internal use by BeginPopup() + ImGuiWindowFlags_Modal = 1 << 26, // Don't use! For internal use by BeginPopupModal() + ImGuiWindowFlags_ChildMenu = 1 << 27 // Don't use! For internal use by BeginMenu() +}; + +// Flags for ImGui::InputText() +enum ImGuiInputTextFlags_ +{ + // Default: 0 + ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ + ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef + ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z + ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs + ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. + ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field + ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, allow exiting edition by pressing Enter. Ctrl+Enter to add new line (by default adds new lines with Enter). + ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally + ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode + ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode + ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() +}; + +// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() +enum ImGuiTreeNodeFlags_ +{ + ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected + ImGuiTreeNodeFlags_Framed = 1 << 1, // Full colored frame (e.g. for CollapsingHeader) + ImGuiTreeNodeFlags_AllowOverlapMode = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one + ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack + ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) + ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open + ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node + ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. + ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). + ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow + //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 10, // FIXME: TODO: Extend hit box horizontally even if not framed + //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 11, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog +}; + +// Flags for ImGui::Selectable() +enum ImGuiSelectableFlags_ +{ + // Default: 0 + ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window + ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) + ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too +}; + +// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array +enum ImGuiKey_ +{ + ImGuiKey_Tab, // for tabbing through fields + ImGuiKey_LeftArrow, // for text edit + ImGuiKey_RightArrow,// for text edit + ImGuiKey_UpArrow, // for text edit + ImGuiKey_DownArrow, // for text edit + ImGuiKey_PageUp, + ImGuiKey_PageDown, + ImGuiKey_Home, // for text edit + ImGuiKey_End, // for text edit + ImGuiKey_Delete, // for text edit + ImGuiKey_Backspace, // for text edit + ImGuiKey_Enter, // for text edit + ImGuiKey_Escape, // for text edit + ImGuiKey_A, // for text edit CTRL+A: select all + ImGuiKey_C, // for text edit CTRL+C: copy + ImGuiKey_V, // for text edit CTRL+V: paste + ImGuiKey_X, // for text edit CTRL+X: cut + ImGuiKey_Y, // for text edit CTRL+Y: redo + ImGuiKey_Z, // for text edit CTRL+Z: undo + ImGuiKey_COUNT +}; + +// Enumeration for PushStyleColor() / PopStyleColor() +enum ImGuiCol_ +{ + ImGuiCol_Text, + ImGuiCol_TextDisabled, + ImGuiCol_WindowBg, // Background of normal windows + ImGuiCol_ChildWindowBg, // Background of child windows + ImGuiCol_PopupBg, // Background of popups, menus, tooltips windows + ImGuiCol_Border, + ImGuiCol_BorderShadow, + ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input + ImGuiCol_FrameBgHovered, + ImGuiCol_FrameBgActive, + ImGuiCol_TitleBg, + ImGuiCol_TitleBgCollapsed, + ImGuiCol_TitleBgActive, + ImGuiCol_MenuBarBg, + ImGuiCol_ScrollbarBg, + ImGuiCol_ScrollbarGrab, + ImGuiCol_ScrollbarGrabHovered, + ImGuiCol_ScrollbarGrabActive, + ImGuiCol_ComboBg, + ImGuiCol_CheckMark, + ImGuiCol_SliderGrab, + ImGuiCol_SliderGrabActive, + ImGuiCol_Button, + ImGuiCol_ButtonHovered, + ImGuiCol_ButtonActive, + ImGuiCol_Header, + ImGuiCol_HeaderHovered, + ImGuiCol_HeaderActive, + ImGuiCol_Column, + ImGuiCol_ColumnHovered, + ImGuiCol_ColumnActive, + ImGuiCol_ResizeGrip, + ImGuiCol_ResizeGripHovered, + ImGuiCol_ResizeGripActive, + ImGuiCol_CloseButton, + ImGuiCol_CloseButtonHovered, + ImGuiCol_CloseButtonActive, + ImGuiCol_PlotLines, + ImGuiCol_PlotLinesHovered, + ImGuiCol_PlotHistogram, + ImGuiCol_PlotHistogramHovered, + ImGuiCol_TextSelectedBg, + ImGuiCol_ModalWindowDarkening, // darken entire screen when a modal window is active + ImGuiCol_COUNT +}; + +// Enumeration for PushStyleVar() / PopStyleVar() +// NB: the enum only refers to fields of ImGuiStyle() which makes sense to be pushed/poped in UI code. Feel free to add others. +enum ImGuiStyleVar_ +{ + ImGuiStyleVar_Alpha, // float + ImGuiStyleVar_WindowPadding, // ImVec2 + ImGuiStyleVar_WindowRounding, // float + ImGuiStyleVar_WindowMinSize, // ImVec2 + ImGuiStyleVar_ChildWindowRounding, // float + ImGuiStyleVar_FramePadding, // ImVec2 + ImGuiStyleVar_FrameRounding, // float + ImGuiStyleVar_ItemSpacing, // ImVec2 + ImGuiStyleVar_ItemInnerSpacing, // ImVec2 + ImGuiStyleVar_IndentSpacing, // float + ImGuiStyleVar_GrabMinSize, // float + ImGuiStyleVar_ButtonTextAlign, // flags ImGuiAlign_* + ImGuiStyleVar_Count_ +}; + +// Enumeration for ColorEditMode() +// FIXME-OBSOLETE: Will be replaced by future color/picker api +enum ImGuiColorEditMode_ +{ + ImGuiColorEditMode_UserSelect = -2, + ImGuiColorEditMode_UserSelectShowButton = -1, + ImGuiColorEditMode_RGB = 0, + ImGuiColorEditMode_HSV = 1, + ImGuiColorEditMode_HEX = 2 +}; + +// Enumeration for GetMouseCursor() +enum ImGuiMouseCursor_ +{ + ImGuiMouseCursor_None = -1, + ImGuiMouseCursor_Arrow = 0, + ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. + ImGuiMouseCursor_Move, // Unused + ImGuiMouseCursor_ResizeNS, // Unused + ImGuiMouseCursor_ResizeEW, // When hovering over a column + ImGuiMouseCursor_ResizeNESW, // Unused + ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window + ImGuiMouseCursor_Count_ +}; + +// Condition flags for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// All those functions treat 0 as a shortcut to ImGuiSetCond_Always +enum ImGuiSetCond_ +{ + ImGuiSetCond_Always = 1 << 0, // Set the variable + ImGuiSetCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call with succeed) + ImGuiSetCond_FirstUseEver = 1 << 2, // Set the variable if the window has no saved data (if doesn't exist in the .ini file) + ImGuiSetCond_Appearing = 1 << 3 // Set the variable if the window is appearing after being hidden/inactive (or the first time) +}; + +struct ImGuiStyle +{ + float Alpha; // Global alpha applies to everything in ImGui + ImVec2 WindowPadding; // Padding within a window + ImVec2 WindowMinSize; // Minimum window size + float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. + float ChildWindowRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows + ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets) + float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). + ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines + ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). + float ColumnsMinSpacing; // Minimum horizontal spacing between two columns + float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar + float ScrollbarRounding; // Radius of grab corners for scrollbar + float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. + float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. + ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. + bool AntiAliasedShapes; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + float CurveTessellationTol; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + ImVec4 Colors[ImGuiCol_COUNT]; + + IMGUI_API ImGuiStyle(); +}; + +// This is where your app communicate with ImGui. Access via ImGui::GetIO(). +// Read 'Programmer guide' section in .cpp file for general usage. +struct ImGuiIO +{ + //------------------------------------------------------------------ + // Settings (fill once) // Default value: + //------------------------------------------------------------------ + + ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. + float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. + const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. + const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging + int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array + float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.020f // When holding a key/button, rate at which it repeats, in seconds. + void* UserData; // = NULL // Store your own data for retrieval by callbacks. + + ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. + float FontGlobalScale; // = 1.0f // Global scale all fonts + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. + ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. + ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. + ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. + ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + + // Advanced/subtle behaviors + bool OSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl + + //------------------------------------------------------------------ + // User Functions + //------------------------------------------------------------------ + + // Rendering function, will be called in Render(). + // Alternatively you can keep this to NULL and call GetDrawData() after Render() to get the same pointer. + // See example applications if you are unsure of how to implement this. + void (*RenderDrawListsFn)(ImDrawData* data); + + // Optional: access OS clipboard + // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) + const char* (*GetClipboardTextFn)(void* user_data); + void (*SetClipboardTextFn)(void* user_data, const char* text); + void* ClipboardUserData; + + // Optional: override memory allocations. MemFreeFn() may be called with a NULL pointer. + // (default to posix malloc/free) + void* (*MemAllocFn)(size_t sz); + void (*MemFreeFn)(void* ptr); + + // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // (default to use native imm32 api on Windows) + void (*ImeSetInputScreenPosFn)(int x, int y); + void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + + //------------------------------------------------------------------ + // Input - Fill before calling NewFrame() + //------------------------------------------------------------------ + + ImVec2 MousePos; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. + bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). + bool KeyCtrl; // Keyboard modifier pressed: Control + bool KeyShift; // Keyboard modifier pressed: Shift + bool KeyAlt; // Keyboard modifier pressed: Alt + bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows + bool KeysDown[512]; // Keyboard keys that are pressed (in whatever storage order you naturally have access to keyboard data) + ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. + + // Functions + IMGUI_API void AddInputCharacter(ImWchar c); // Helper to add a new character into InputCharacters[] + IMGUI_API void AddInputCharactersUTF8(const char* utf8_chars); // Helper to add new characters into InputCharacters[] from an UTF-8 string + inline void ClearInputCharacters() { InputCharacters[0] = 0; } // Helper to clear the text input buffer + + //------------------------------------------------------------------ + // Output - Retrieve after calling NewFrame(), you can use them to discard inputs or hide them from the rest of your application + //------------------------------------------------------------------ + + bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) + bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) + bool WantTextInput; // Some text input widget is active, which will read input characters from the InputCharacters array. + float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames + int MetricsAllocs; // Number of active memory allocations + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsActiveWindows; // Number of visible windows (exclude child windows) + + //------------------------------------------------------------------ + // [Private] ImGui will maintain those fields. Forward compatibility not guaranteed! + //------------------------------------------------------------------ + + ImVec2 MousePosPrev; // Previous mouse position + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are negative to allow mouse enabling/disabling. + bool MouseClicked[5]; // Mouse button went from !Down to Down + ImVec2 MouseClickedPos[5]; // Position at time of clicking + float MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the click point + float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) + float KeysDownDurationPrev[512]; // Previous duration the key has been down + + IMGUI_API ImGuiIO(); +}; + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +// Lightweight std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). +// Our implementation does NOT call c++ constructors because we don't use them in ImGui. Don't use this class as a straight std::vector replacement in your code! +template +class ImVector +{ +public: + int Size; + int Capacity; + T* Data; + + typedef T value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + ImVector() { Size = Capacity = 0; Data = NULL; } + ~ImVector() { if (Data) ImGui::MemFree(Data); } + + inline bool empty() const { return Size == 0; } + inline int size() const { return Size; } + inline int capacity() const { return Capacity; } + + inline value_type& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } + inline const value_type& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } + + inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } + inline iterator begin() { return Data; } + inline const_iterator begin() const { return Data; } + inline iterator end() { return Data + Size; } + inline const_iterator end() const { return Data + Size; } + inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } + inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } + inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size-1]; } + inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; } + inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } + + inline int _grow_capacity(int new_size) { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > new_size ? new_capacity : new_size; } + + inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } + inline void reserve(int new_capacity) + { + if (new_capacity <= Capacity) return; + T* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(value_type)); + if (Data) + memcpy(new_data, Data, (size_t)Size * sizeof(value_type)); + ImGui::MemFree(Data); + Data = new_data; + Capacity = new_capacity; + } + + inline void push_back(const value_type& v) { if (Size == Capacity) reserve(_grow_capacity(Size+1)); Data[Size++] = v; } + inline void pop_back() { IM_ASSERT(Size > 0); Size--; } + + inline iterator erase(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } + inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } +}; + +// Helper: execute a block of code at maximum once a frame +// Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. +// Usage: +// IMGUI_ONCE_UPON_A_FRAME +// { +// // code block will be executed one per frame +// } +// Attention! the macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. +#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf##__LINE__; if (imgui_oaf##__LINE__) +struct ImGuiOnceUponAFrame +{ + ImGuiOnceUponAFrame() { RefFrame = -1; } + mutable int RefFrame; + operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } +}; + +// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +struct ImGuiTextFilter +{ + struct TextRange + { + const char* b; + const char* e; + + TextRange() { b = e = NULL; } + TextRange(const char* _b, const char* _e) { b = _b; e = _e; } + const char* begin() const { return b; } + const char* end() const { return e; } + bool empty() const { return b == e; } + char front() const { return *b; } + static bool is_blank(char c) { return c == ' ' || c == '\t'; } + void trim_blanks() { while (b < e && is_blank(*b)) b++; while (e > b && is_blank(*(e-1))) e--; } + IMGUI_API void split(char separator, ImVector& out); + }; + + char InputBuf[256]; + ImVector Filters; + int CountGrep; + + ImGuiTextFilter(const char* default_filter = ""); + ~ImGuiTextFilter() {} + void Clear() { InputBuf[0] = 0; Build(); } + bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build + bool PassFilter(const char* text, const char* text_end = NULL) const; + bool IsActive() const { return !Filters.empty(); } + IMGUI_API void Build(); +}; + +// Helper: Text buffer for logging/accumulating text +struct ImGuiTextBuffer +{ + ImVector Buf; + + ImGuiTextBuffer() { Buf.push_back(0); } + inline char operator[](int i) { return Buf.Data[i]; } + const char* begin() const { return &Buf.front(); } + const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator + int size() const { return Buf.Size - 1; } + bool empty() { return Buf.Size <= 1; } + void clear() { Buf.clear(); Buf.push_back(0); } + const char* c_str() const { return Buf.Data; } + IMGUI_API void append(const char* fmt, ...) IM_PRINTFARGS(2); + IMGUI_API void appendv(const char* fmt, va_list args); +}; + +// Helper: Simple Key->value storage +// Typically you don't have to worry about this since a storage is held within each Window. +// We use it to e.g. store collapse state for a tree (Int 0/1), store color edit options. +// You can use it as custom user storage for temporary values. +// Declare your own storage if: +// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). +// - You want to store custom debug data easily without adding or editing structures in your code. +// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types. +struct ImGuiStorage +{ + struct Pair + { + ImGuiID key; + union { int val_i; float val_f; void* val_p; }; + Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } + Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } + Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } + }; + ImVector Data; + + // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) + // - Set***() functions find pair, insertion on demand if missing. + // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair. + IMGUI_API void Clear(); + IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; + IMGUI_API void SetInt(ImGuiID key, int val); + IMGUI_API bool GetBool(ImGuiID key, bool default_val = false) const; + IMGUI_API void SetBool(ImGuiID key, bool val); + IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; + IMGUI_API void SetFloat(ImGuiID key, float val); + IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL + IMGUI_API void SetVoidPtr(ImGuiID key, void* val); + + // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. + // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. + // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct) + // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; + IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); + IMGUI_API bool* GetBoolRef(ImGuiID key, bool default_val = false); + IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); + IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); + + // Use on your own storage if you know only integer are being stored (open/close all tree nodes) + IMGUI_API void SetAllInt(int val); +}; + +// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. +struct ImGuiTextEditCallbackData +{ + ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + bool ReadOnly; // Read-only mode // Read-only + + // CharFilter event: + ImWchar EventChar; // Character input // Read-write (replace character or set to zero) + + // Completion,History,Always events: + // If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only + char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) + int BufTextLen; // Current text length in bytes // Read-write + int BufSize; // Maximum text length in bytes // Read-only + bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write + int CursorPos; // // Read-write + int SelectionStart; // // Read-write (== to SelectionEnd when no selection) + int SelectionEnd; // // Read-write + + // NB: Helper functions for text manipulation. Calling those function loses selection. + void DeleteChars(int pos, int bytes_count); + void InsertChars(int pos, const char* text, const char* text_end = NULL); + bool HasSelection() const { return SelectionStart != SelectionEnd; } +}; + +// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). +// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. +struct ImGuiSizeConstraintCallbackData +{ + void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() + ImVec2 Pos; // Read-only. Window position, for reference. + ImVec2 CurrentSize; // Read-only. Current window size. + ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. +}; + +// Helpers macros to generate 32-bits encoded colors +#ifdef IMGUI_USE_BGRA_PACKED_COLOR +#define IM_COL32_R_SHIFT 16 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 0 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#else +#define IM_COL32_R_SHIFT 0 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 16 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#endif +#define IM_COL32(R,G,B,A) (((ImU32)(A)<>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; } + ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } + ImColor(const ImVec4& col) { Value = col; } + inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } + inline operator ImVec4() const { return Value; } + + inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } + + static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } +}; + +// Helper: Manually clip large list of items. +// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. +// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. +// ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. +// Usage: +// ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. +// while (clipper.Step()) +// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) +// ImGui::Text("line number %d", i); +// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). +// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. +// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) +// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. +struct ImGuiListClipper +{ + float StartPosY; + float ItemsHeight; + int ItemsCount, StepNo, DisplayStart, DisplayEnd; + + // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). + // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetItemsLineHeightWithSpacing(). + // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). + ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). + ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. + + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. +}; + +//----------------------------------------------------------------------------- +// Draw List +// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. +//----------------------------------------------------------------------------- + +// Draw callbacks for advanced uses. +// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) +// Draw callback may be useful for example, A) Change your GPU render state, B) render a complex 3D scene inside a UI element (without an intermediate texture/render target), etc. +// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) cmd.UserCallback(parent_list, cmd); else RenderTriangles()' +typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); + +// Typically, 1 command = 1 gpu draw call (unless command is a callback) +struct ImDrawCmd +{ + unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. + ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2) + ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. + void* UserCallbackData; // The draw callback code can access this. + + ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = -8192.0f; ClipRect.z = ClipRect.w = +8192.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; } +}; + +// Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; +#endif + +// Vertex layout +#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT +struct ImDrawVert +{ + ImVec2 pos; + ImVec2 uv; + ImU32 col; +}; +#else +// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h +// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. +// The type has to be described within the macro (you can either declare the struct or use a typedef) +IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; +#endif + +// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. +// You can also use them to simulate drawing layers and submit primitives in a different order than how they will be rendered. +struct ImDrawChannel +{ + ImVector CmdBuffer; + ImVector IdxBuffer; +}; + +// Draw command list +// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. +// At the moment, each ImGui window contains its own ImDrawList but they could potentially be merged in the future. +// If you want to add custom rendering within a window, you can use ImGui::GetWindowDrawList() to access the current draw list and add your own primitives. +// You can interleave normal ImGui:: calls and adding primitives to the current draw list. +// All positions are in screen coordinates (0,0=top-left, 1 pixel per unit). Primitives are always added to the list and not culled (culling is done at render time and at a higher-level by ImGui:: functions). +struct ImDrawList +{ + // This is what you have to render + ImVector CmdBuffer; // Commands. Typically 1 command = 1 gpu draw call. + ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those + ImVector VtxBuffer; // Vertex buffer. + + // [Internal, used while building lists] + const char* _OwnerName; // Pointer to owner window's name for debugging + unsigned int _VtxCurrentIdx; // [Internal] == VtxBuffer.Size + ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImVector _ClipRectStack; // [Internal] + ImVector _TextureIdStack; // [Internal] + ImVector _Path; // [Internal] current path building + int _ChannelsCurrent; // [Internal] current channel number (0) + int _ChannelsCount; // [Internal] number of active channels (1+) + ImVector _Channels; // [Internal] draw channels for columns API (not resized down so _ChannelsCount may be smaller than _Channels.Size) + + ImDrawList() { _OwnerName = NULL; Clear(); } + ~ImDrawList() { ClearFreeMemory(); } + IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) + IMGUI_API void PushClipRectFullScreen(); + IMGUI_API void PopClipRect(); + IMGUI_API void PushTextureID(const ImTextureID& texture_id); + IMGUI_API void PopTextureID(); + + // Primitives + IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0, float thickness = 1.0f); // a: upper-left, b: lower-right, rounding_corners_flags: 4-bits corresponding to which corner to round + IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0); // a: upper-left, b: lower-right + IMGUI_API void AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); + IMGUI_API void AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); + IMGUI_API void AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); + IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f); + IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); + IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); + IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); + IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); + IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased); + IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); + + // Stateful path API, add points then finish with PathFill() or PathStroke() + inline void PathClear() { _Path.resize(0); } + inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } + inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } + inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); } + inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); } + IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); + IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0); + IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners_flags = ~0); // rounding_corners_flags: 4-bits corresponding to which corner to round + + // Channels + // - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) + // - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end) + IMGUI_API void ChannelsSplit(int channels_count); + IMGUI_API void ChannelsMerge(); + IMGUI_API void ChannelsSetCurrent(int channel_index); + + // Advanced + IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. + IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + + // Internal helpers + // NB: all primitives needs to be reserved via PrimReserve() beforehand! + IMGUI_API void Clear(); + IMGUI_API void ClearFreeMemory(); + IMGUI_API void PrimReserve(int idx_count, int vtx_count); + IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) + IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); + IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); + inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } + inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } + inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } + IMGUI_API void UpdateClipRect(); + IMGUI_API void UpdateTextureID(); +}; + +// All draw data to render an ImGui frame +struct ImDrawData +{ + bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + ImDrawList** CmdLists; + int CmdListsCount; + int TotalVtxCount; // For convenience, sum of all cmd_lists vtx_buffer.Size + int TotalIdxCount; // For convenience, sum of all cmd_lists idx_buffer.Size + + // Functions + ImDrawData() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; } + IMGUI_API void DeIndexAllBuffers(); // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +}; + +struct ImFontConfig +{ + void* FontData; // // TTF data + int FontDataSize; // // TTF data size + bool FontDataOwnedByAtlas; // true // TTF data ownership taken by the container ImFontAtlas (will delete memory itself). Set to true + int FontNo; // 0 // Index of font within TTF file + float SizePixels; // // Size in pixels for rasterizer + int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. + ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs + const ImWchar* GlyphRanges; // // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). + bool MergeGlyphCenterV; // false // When merging (multiple ImFontInput for one ImFont), vertically center new glyphs instead of aligning their baseline + + // [Internal] + char Name[32]; // Name (strictly for debugging) + ImFont* DstFont; + + IMGUI_API ImFontConfig(); +}; + +// Load and rasterize multiple TTF fonts into a same texture. +// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. +// We also add custom graphic data into the texture that serves for ImGui. +// 1. (Optional) Call AddFont*** functions. If you don't call any, the default font will be loaded for you. +// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. +// 3. Upload the pixels data into a texture within your graphics system. +// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. +// 5. Call ClearTexData() to free textures memory on the heap. +// NB: If you use a 'glyph_ranges' array you need to make sure that your array persist up until the ImFont is cleared. We only copy the pointer, not the data. +struct ImFontAtlas +{ + IMGUI_API ImFontAtlas(); + IMGUI_API ~ImFontAtlas(); + IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); + IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); + IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); + IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() + IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data' still owned by caller. Compress with binary_to_compressed_c.cpp + IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 paramaeter + IMGUI_API void ClearTexData(); // Clear the CPU-side texture data. Saves RAM once the texture has been copied to graphics memory. + IMGUI_API void ClearInputData(); // Clear the input TTF data (inc sizes, glyph ranges) + IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates) + IMGUI_API void Clear(); // Clear all + + // Retrieve texture data + // User is in charge of copying the pixels into graphics memory, then call SetTextureUserID() + // After loading the texture into your graphic system, store your texture handle in 'TexID' (ignore if you aren't using multiple fonts nor images) + // RGBA32 format is provided for convenience and high compatibility, but note that all RGB pixels are white, so 75% of the memory is wasted. + // Pitch = Width * BytesPerPixels + IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel + IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + void SetTexID(void* id) { TexID = id; } + + // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) + // NB: Make sure that your string are UTF-8 and NOT in your local code page. See FAQ for details. + IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin + IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters + IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Japanese + full set of about 21000 CJK Unified Ideographs + IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters + IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters + + // Members + // (Access texture data via GetTexData*() calls which will setup a default font for you.) + void* TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It ia passed back to you during rendering. + unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight + unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 + int TexWidth; // Texture width calculated during Build(). + int TexHeight; // Texture height calculated during Build(). + int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. + ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel + ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. + + // Private + ImVector ConfigData; // Internal data + IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. + IMGUI_API void RenderCustomTexData(int pass, void* rects); +}; + +// Font runtime data and rendering +// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). +struct ImFont +{ + struct Glyph + { + ImWchar Codepoint; + float XAdvance; + float X0, Y0, X1, Y1; + float U0, V0, U1, V1; // Texture coordinates + }; + + // Members: Hot ~62/78 bytes + float FontSize; // // Height of characters, set during loading (don't change after loading) + float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() + ImVec2 DisplayOffset; // = (0.f,1.f) // Offset font rendering by xx pixels + ImVector Glyphs; // // All glyphs. + ImVector IndexXAdvance; // // Sparse. Glyphs->XAdvance in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). + ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. + const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackXAdvance; // == FallbackGlyph->XAdvance + ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + + // Members: Cold ~18/26 bytes + short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData + ImFontAtlas* ContainerAtlas; // // What we has been loaded into + float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + + // Methods + IMGUI_API ImFont(); + IMGUI_API ~ImFont(); + IMGUI_API void Clear(); + IMGUI_API void BuildLookupTable(); + IMGUI_API const Glyph* FindGlyph(ImWchar c) const; + IMGUI_API void SetFallbackChar(ImWchar c); + float GetCharAdvance(ImWchar c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } + bool IsLoaded() const { return ContainerAtlas != NULL; } + + // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. + // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. + IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 + IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; + IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; + + // Private + IMGUI_API void GrowIndex(int new_size); + IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. +}; + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) +#ifdef IMGUI_INCLUDE_IMGUI_USER_H +#include "imgui_user.h" +#endif diff --git a/3rdparty/imgui/include/imgui_internal.h b/3rdparty/imgui/include/imgui_internal.h new file mode 100644 index 0000000..c9dcb0d --- /dev/null +++ b/3rdparty/imgui/include/imgui_internal.h @@ -0,0 +1,776 @@ +// dear imgui, v1.50 WIP +// (internals) + +// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! +// Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) +// #define IMGUI_DEFINE_MATH_OPERATORS + +#pragma once + +#ifndef IMGUI_VERSION +#error Must include imgui.h before imgui_internal.h +#endif + +#include // FILE* +#include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + +//----------------------------------------------------------------------------- +// Forward Declarations +//----------------------------------------------------------------------------- + +struct ImRect; +struct ImGuiColMod; +struct ImGuiStyleMod; +struct ImGuiGroupData; +struct ImGuiSimpleColumns; +struct ImGuiDrawContext; +struct ImGuiTextEditState; +struct ImGuiIniData; +struct ImGuiMouseCursorData; +struct ImGuiPopupRef; +struct ImGuiWindow; + +typedef int ImGuiLayoutType; // enum ImGuiLayoutType_ +typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ +typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ +typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ + +//------------------------------------------------------------------------- +// STB libraries +//------------------------------------------------------------------------- + +namespace ImGuiStb +{ + +#undef STB_TEXTEDIT_STRING +#undef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_CHARTYPE ImWchar +#define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f +#include "stb_textedit.h" + +} // namespace ImGuiStb + +//----------------------------------------------------------------------------- +// Context +//----------------------------------------------------------------------------- + +#ifndef GImGui +extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer +#endif + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) +#define IM_PI 3.14159265358979323846f +#define IM_OFFSETOF(_TYPE,_ELM) ((size_t)&(((_TYPE*)0)->_ELM)) + +// Helpers: UTF-8 <> wchar +IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points + +// Helpers: Misc +IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings +IMGUI_API void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); +IMGUI_API FILE* ImOpenFile(const char* filename, const char* file_open_mode); +IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c); +static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } + +// Helpers: String +IMGUI_API int ImStricmp(const char* str1, const char* str2); +IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); +IMGUI_API char* ImStrdup(const char* str); +IMGUI_API int ImStrlenW(const ImWchar* str); +IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line +IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); +IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3); +IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args); + +// Helpers: Math +// We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined) +#ifdef IMGUI_DEFINE_MATH_OPERATORS +static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } +static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } +static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); } +static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); } +static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); } +static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); } +static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } +static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } +static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } +static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } +static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); } +#endif + +static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } +static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } +static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; } +static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } +static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); } +static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); } +static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } +static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } +static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } +static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } +static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } +static inline float ImFloor(float f) { return (float)(int)f; } +static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } + +// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. +// Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. +#ifdef IMGUI_DEFINE_PLACEMENT_NEW +struct ImPlacementNewDummy {}; +inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } +inline void operator delete(void*, ImPlacementNewDummy, void*) {} +#define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) +#endif + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +enum ImGuiButtonFlags_ +{ + ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat + ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set) + ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release) + ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release) + ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release) + ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping + ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press + ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction + ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only + ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held + ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable +}; + +enum ImGuiSliderFlags_ +{ + ImGuiSliderFlags_Vertical = 1 << 0 +}; + +enum ImGuiSelectableFlagsPrivate_ +{ + // NB: need to be in sync with last value of ImGuiSelectableFlags_ + ImGuiSelectableFlags_Menu = 1 << 3, + ImGuiSelectableFlags_MenuItem = 1 << 4, + ImGuiSelectableFlags_Disabled = 1 << 5, + ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 +}; + +// FIXME: this is in development, not exposed/functional as a generic feature yet. +enum ImGuiLayoutType_ +{ + ImGuiLayoutType_Vertical, + ImGuiLayoutType_Horizontal +}; + +enum ImGuiPlotType +{ + ImGuiPlotType_Lines, + ImGuiPlotType_Histogram +}; + +enum ImGuiDataType +{ + ImGuiDataType_Int, + ImGuiDataType_Float, + ImGuiDataType_Float2, +}; + +enum ImGuiCorner +{ + ImGuiCorner_TopLeft = 1 << 0, // 1 + ImGuiCorner_TopRight = 1 << 1, // 2 + ImGuiCorner_BottomRight = 1 << 2, // 4 + ImGuiCorner_BottomLeft = 1 << 3, // 8 + ImGuiCorner_All = 0x0F +}; + +// 2D axis aligned bounding-box +// NB: we can't rely on ImVec2 math operators being available here +struct IMGUI_API ImRect +{ + ImVec2 Min; // Upper-left + ImVec2 Max; // Lower-right + + ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} + ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} + ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} + ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} + + ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); } + ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); } + float GetWidth() const { return Max.x-Min.x; } + float GetHeight() const { return Max.y-Min.y; } + ImVec2 GetTL() const { return Min; } // Top-left + ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right + ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left + ImVec2 GetBR() const { return Max; } // Bottom-right + bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } + bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } + bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } + void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; } + void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; } + void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } + void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } + void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } + void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } + void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } + ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const + { + if (!on_edge && Contains(p)) + return p; + if (p.x > Max.x) p.x = Max.x; + else if (p.x < Min.x) p.x = Min.x; + if (p.y > Max.y) p.y = Max.y; + else if (p.y < Min.y) p.y = Min.y; + return p; + } +}; + +// Stacked color modifier, backup of modified data so we can restore it +struct ImGuiColMod +{ + ImGuiCol Col; + ImVec4 BackupValue; +}; + +// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. +struct ImGuiStyleMod +{ + ImGuiStyleVar VarIdx; + union { int BackupInt[2]; float BackupFloat[2]; }; + ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } +}; + +// Stacked data for BeginGroup()/EndGroup() +struct ImGuiGroupData +{ + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + float BackupIndentX; + float BackupGroupOffsetX; + float BackupCurrentLineHeight; + float BackupCurrentLineTextBaseOffset; + float BackupLogLinePosY; + bool BackupActiveIdIsAlive; + bool AdvanceCursor; +}; + +// Per column data for Columns() +struct ImGuiColumnData +{ + float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) + //float IndentX; +}; + +// Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper. +struct IMGUI_API ImGuiSimpleColumns +{ + int Count; + float Spacing; + float Width, NextWidth; + float Pos[8], NextWidths[8]; + + ImGuiSimpleColumns(); + void Update(int count, float spacing, bool clear); + float DeclColumns(float w0, float w1, float w2); + float CalcExtraSpace(float avail_w); +}; + +// Internal state of the currently focused/edited text input box +struct IMGUI_API ImGuiTextEditState +{ + ImGuiID Id; // widget id owning the text state + ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. + ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + ImVector TempTextBuffer; + int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + int BufSizeA; // end-user buffer size + float ScrollX; + ImGuiStb::STB_TexteditState StbState; + float CursorAnim; + bool CursorFollow; + bool SelectedAllMouseLock; + + ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } + void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking + void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } + bool HasSelection() const { return StbState.select_start != StbState.select_end; } + void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } + void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; } + void OnKeyPressed(int key); +}; + +// Data saved in imgui.ini file +struct ImGuiIniData +{ + char* Name; + ImGuiID Id; + ImVec2 Pos; + ImVec2 Size; + bool Collapsed; +}; + +// Mouse cursor data (used when io.MouseDrawCursor is set) +struct ImGuiMouseCursorData +{ + ImGuiMouseCursor Type; + ImVec2 HotOffset; + ImVec2 Size; + ImVec2 TexUvMin[2]; + ImVec2 TexUvMax[2]; +}; + +// Storage for current popup stack +struct ImGuiPopupRef +{ + ImGuiID PopupId; // Set on OpenPopup() + ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() + ImGuiWindow* ParentWindow; // Set on OpenPopup() + ImGuiID ParentMenuSet; // Set on OpenPopup() + ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup + + ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; } +}; + +// Main state for ImGui +struct ImGuiContext +{ + bool Initialized; + ImGuiIO IO; + ImGuiStyle Style; + ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() + float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() + float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. + ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel + + float Time; + int FrameCount; + int FrameCountEnded; + int FrameCountRendered; + ImVector Windows; + ImVector WindowsSortBuffer; + ImGuiWindow* CurrentWindow; // Being drawn into + ImVector CurrentWindowStack; + ImGuiWindow* FocusedWindow; // Will catch keyboard inputs + ImGuiWindow* HoveredWindow; // Will catch mouse inputs + ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) + ImGuiID HoveredId; // Hovered widget + bool HoveredIdAllowOverlap; + ImGuiID HoveredIdPreviousFrame; + ImGuiID ActiveId; // Active widget + ImGuiID ActiveIdPreviousFrame; + bool ActiveIdIsAlive; + bool ActiveIdIsJustActivated; // Set at the time of activation for one frame + bool ActiveIdAllowOverlap; // Set only by active widget + ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) + ImGuiWindow* ActiveIdWindow; + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. + ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId + ImVector Settings; // .ini Settings + float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero + ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() + ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() + ImVector FontStack; // Stack for PushFont()/PopFont() + ImVector OpenPopupStack; // Which popups are open (persistent) + ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) + + // Storage for SetNexWindow** and SetNextTreeNode*** functions + ImVec2 SetNextWindowPosVal; + ImVec2 SetNextWindowSizeVal; + ImVec2 SetNextWindowContentSizeVal; + bool SetNextWindowCollapsedVal; + ImGuiSetCond SetNextWindowPosCond; + ImGuiSetCond SetNextWindowSizeCond; + ImGuiSetCond SetNextWindowContentSizeCond; + ImGuiSetCond SetNextWindowCollapsedCond; + ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true + ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback; + void* SetNextWindowSizeConstraintCallbackUserData; + bool SetNextWindowSizeConstraint; + bool SetNextWindowFocus; + bool SetNextTreeNodeOpenVal; + ImGuiSetCond SetNextTreeNodeOpenCond; + + // Render + ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user + ImVector RenderDrawLists[3]; + float ModalWindowDarkeningRatio; + ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays + ImGuiMouseCursor MouseCursor; + ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_]; + + // Widget state + ImGuiTextEditState InputTextState; + ImFont InputTextPasswordFont; + ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode + float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings + ImVec2 DragLastMouseDelta; + float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio + float DragSpeedScaleSlow; + float DragSpeedScaleFast; + ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? + char Tooltip[1024]; + char* PrivateClipboard; // If no custom clipboard handler is defined + ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor + + // Logging + bool LogEnabled; + FILE* LogFile; // If != NULL log to stdout/ file + ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + int LogStartDepth; + int LogAutoExpandMaxDepth; + + // Misc + float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + int FramerateSecPerFrameIdx; + float FramerateSecPerFrameAccum; + int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags + int CaptureKeyboardNextFrame; + char TempBuffer[1024*3+1]; // temporary text buffer + + ImGuiContext() + { + Initialized = false; + Font = NULL; + FontSize = FontBaseSize = 0.0f; + FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); + + Time = 0.0f; + FrameCount = 0; + FrameCountEnded = FrameCountRendered = -1; + CurrentWindow = NULL; + FocusedWindow = NULL; + HoveredWindow = NULL; + HoveredRootWindow = NULL; + HoveredId = 0; + HoveredIdAllowOverlap = false; + HoveredIdPreviousFrame = 0; + ActiveId = 0; + ActiveIdPreviousFrame = 0; + ActiveIdIsAlive = false; + ActiveIdIsJustActivated = false; + ActiveIdAllowOverlap = false; + ActiveIdClickOffset = ImVec2(-1,-1); + ActiveIdWindow = NULL; + MovedWindow = NULL; + MovedWindowMoveId = 0; + SettingsDirtyTimer = 0.0f; + + SetNextWindowPosVal = ImVec2(0.0f, 0.0f); + SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); + SetNextWindowCollapsedVal = false; + SetNextWindowPosCond = 0; + SetNextWindowSizeCond = 0; + SetNextWindowContentSizeCond = 0; + SetNextWindowCollapsedCond = 0; + SetNextWindowSizeConstraintRect = ImRect(); + SetNextWindowSizeConstraintCallback = NULL; + SetNextWindowSizeConstraintCallbackUserData = NULL; + SetNextWindowSizeConstraint = false; + SetNextWindowFocus = false; + SetNextTreeNodeOpenVal = false; + SetNextTreeNodeOpenCond = 0; + + ScalarAsInputTextId = 0; + DragCurrentValue = 0.0f; + DragLastMouseDelta = ImVec2(0.0f, 0.0f); + DragSpeedDefaultRatio = 1.0f / 100.0f; + DragSpeedScaleSlow = 0.01f; + DragSpeedScaleFast = 10.0f; + ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); + memset(Tooltip, 0, sizeof(Tooltip)); + PrivateClipboard = NULL; + OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); + + ModalWindowDarkeningRatio = 0.0f; + OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging + MouseCursor = ImGuiMouseCursor_Arrow; + memset(MouseCursorData, 0, sizeof(MouseCursorData)); + + LogEnabled = false; + LogFile = NULL; + LogClipboard = NULL; + LogStartDepth = 0; + LogAutoExpandMaxDepth = 2; + + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); + FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameAccum = 0.0f; + CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1; + memset(TempBuffer, 0, sizeof(TempBuffer)); + } +}; + +// Transient per-window data, reset at the beginning of the frame +// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. +struct IMGUI_API ImGuiDrawContext +{ + ImVec2 CursorPos; + ImVec2 CursorPosPrevLine; + ImVec2 CursorStartPos; + ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame + float CurrentLineHeight; + float CurrentLineTextBaseOffset; + float PrevLineHeight; + float PrevLineTextBaseOffset; + float LogLinePosY; + int TreeDepth; + ImGuiID LastItemId; + ImRect LastItemRect; + bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) + bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) + bool MenuBarAppending; + float MenuBarOffsetX; + ImVector ChildWindows; + ImGuiStorage* StateStorage; + ImGuiLayoutType LayoutType; + + // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. + float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window + float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] + bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] + bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] + ImVector ItemWidthStack; + ImVector TextWrapPosStack; + ImVector AllowKeyboardFocusStack; + ImVector ButtonRepeatStack; + ImVectorGroupStack; + ImGuiColorEditMode ColorEditMode; + int StackSizesBackup[6]; // Store size of various stacks for asserting + + float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) + float GroupOffsetX; + float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. + int ColumnsCurrent; + int ColumnsCount; + float ColumnsMinX; + float ColumnsMaxX; + float ColumnsStartPosY; + float ColumnsCellMinY; + float ColumnsCellMaxY; + bool ColumnsShowBorders; + ImGuiID ColumnsSetId; + ImVector ColumnsData; + + ImGuiDrawContext() + { + CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); + CurrentLineHeight = PrevLineHeight = 0.0f; + CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; + LogLinePosY = -1.0f; + TreeDepth = 0; + LastItemId = 0; + LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); + LastItemHoveredAndUsable = LastItemHoveredRect = false; + MenuBarAppending = false; + MenuBarOffsetX = 0.0f; + StateStorage = NULL; + LayoutType = ImGuiLayoutType_Vertical; + ItemWidth = 0.0f; + ButtonRepeat = false; + AllowKeyboardFocus = true; + TextWrapPos = -1.0f; + ColorEditMode = ImGuiColorEditMode_RGB; + memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); + + IndentX = 0.0f; + GroupOffsetX = 0.0f; + ColumnsOffsetX = 0.0f; + ColumnsCurrent = 0; + ColumnsCount = 1; + ColumnsMinX = ColumnsMaxX = 0.0f; + ColumnsStartPosY = 0.0f; + ColumnsCellMinY = ColumnsCellMaxY = 0.0f; + ColumnsShowBorders = true; + ColumnsSetId = 0; + } +}; + +// Windows data +struct IMGUI_API ImGuiWindow +{ + char* Name; + ImGuiID ID; // == ImHash(Name) + ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. + ImVec2 PosFloat; + ImVec2 Pos; // Position rounded-up to nearest pixel + ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) + ImVec2 SizeFull; // Size when non collapsed + ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame + ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() + ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis + ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect + ImGuiID MoveId; // == window->GetID("#MOVE") + ImVec2 Scroll; + ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) + ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered + bool ScrollbarX, ScrollbarY; + ImVec2 ScrollbarSizes; + float BorderSize; + bool Active; // Set to true on Begin() + bool WasActive; + bool Accessed; // Set to true when any widget access the current window + bool Collapsed; // Set when collapsing window to become only title-bar + bool SkipItems; // == Visible && !Collapsed + int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) + int AutoFitFramesX, AutoFitFramesY; + bool AutoFitOnlyGrows; + int AutoPosLastDirection; + int HiddenFrames; + int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. + int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag. + int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag. + bool SetWindowPosCenterWanted; + + ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame + ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack + ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. + ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. + int LastFrameActive; + float ItemWidthDefault; + ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items + ImGuiStorage StateStorage; + float FontWindowScale; // Scale multiplier per-window + ImDrawList* DrawList; + ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. + ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. + ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL. + + // Navigation / Focus + int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() + int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) + int FocusIdxAllRequestCurrent; // Item being requested for focus + int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus + int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) + int FocusIdxTabRequestNext; // " + +public: + ImGuiWindow(const char* name); + ~ImGuiWindow(); + + ImGuiID GetID(const char* str, const char* str_end = NULL); + ImGuiID GetID(const void* ptr); + ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } + float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } + float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } + ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } + float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } + ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } +}; + +//----------------------------------------------------------------------------- +// Internal API +// No guarantee of forward compatibility here. +//----------------------------------------------------------------------------- + +namespace ImGui +{ + // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) + // If this ever crash because g.CurrentWindow is NULL it means that either + // - ImGui::NewFrame() has never been called, which is illegal. + // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. + inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } + inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* GetParentWindow(); + IMGUI_API ImGuiWindow* FindWindowByName(const char* name); + IMGUI_API void FocusWindow(ImGuiWindow* window); + + IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! + + IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); + IMGUI_API void SetHoveredID(ImGuiID id); + IMGUI_API void KeepAliveID(ImGuiID id); + + IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); + IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); + IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id); + IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); + IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false); + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested + IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); + IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); + IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); + + IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); + + // NB: All position are in absolute pixels coordinates (not window coordinates) + // FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. + // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers. + IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); + IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); + IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); + IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f); + IMGUI_API void RenderBullet(ImVec2 pos); + IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); + IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. + + IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); + IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); + + IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); + IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power); + IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format); + + IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); + IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power); + IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format); + + IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); + + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging + IMGUI_API void TreePushRawID(ImGuiID id); + + IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); + + IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value); + IMGUI_API float RoundScalar(float value, int decimal_precision); + +} // namespace ImGui + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + diff --git a/3rdparty/imgui/include/imgui_stl.h b/3rdparty/imgui/include/imgui_stl.h new file mode 100644 index 0000000..e0533cd --- /dev/null +++ b/3rdparty/imgui/include/imgui_stl.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +//from https://eliasdaler.github.io/using-imgui-with-sfml-pt2/ +namespace ImGui +{ + static auto vector_getter = [](void* vec, int idx, const char** out_text) + { + auto& vector = *static_cast*>(vec); + if (idx < 0 || idx >= static_cast(vector.size())) { return false; } + *out_text = vector.at(idx).c_str(); + return true; + }; + + bool Combo(const char* label, int* currIndex, std::vector& values) + { + if (values.empty()) { return false; } + return Combo(label, currIndex, vector_getter, + static_cast(&values), values.size()); + } + + bool ListBox(const char* label, int* currIndex, std::vector& values) + { + if (values.empty()) { return false; } + return ListBox(label, currIndex, vector_getter, + static_cast(&values), values.size()); + } +} \ No newline at end of file diff --git a/3rdparty/imgui/src/imgui.cpp b/3rdparty/imgui/src/imgui.cpp new file mode 100644 index 0000000..e8d5975 --- /dev/null +++ b/3rdparty/imgui/src/imgui.cpp @@ -0,0 +1,9849 @@ +// dear imgui, v1.50 WIP +// (main code and documentation) + +// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code. +// Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase. +// Get latest version at https://github.com/ocornut/imgui +// Releases change-log at https://github.com/ocornut/imgui/releases +// Developed by Omar Cornut and every direct or indirect contributors to the GitHub. +// This library is free but I need your support to sustain development and maintenance. +// If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui + +/* + + Index + - MISSION STATEMENT + - END-USER GUIDE + - PROGRAMMER GUIDE (read me!) + - API BREAKING CHANGES (read me when you update!) + - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + - How can I help? + - How do I update to a newer version of ImGui? + - What is ImTextureID and how do I display an image? + - I integrated ImGui in my engine and the text or lines are blurry.. + - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around.. + - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs. + - How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application? + - How can I load a different font than the default? + - How can I easily use icons in my application? + - How can I load multiple fonts? + - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? + - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) + - ISSUES & TODO-LIST + - CODE + + + MISSION STATEMENT + ================= + + - easy to use to create code-driven and data-driven tools + - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools + - easy to hack and improve + - minimize screen real-estate usage + - minimize setup and maintenance + - minimize state storage on user side + - portable, minimize dependencies, run on target (consoles, phones, etc.) + - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything) + - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html + + Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: + - doesn't look fancy, doesn't animate + - limited layout features, intricate layouts are typically crafted in code + - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction. + + + END-USER GUIDE + ============== + + - double-click title bar to collapse window + - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin() + - click and drag on lower right corner to resize window + - click and drag on any empty space to move window + - double-click/double-tap on lower right corner grip to auto-fit to content + - TAB/SHIFT+TAB to cycle through keyboard editable fields + - use mouse wheel to scroll + - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true) + - CTRL+Click on a slider or drag box to input value as text + - text editor: + - Hold SHIFT or use mouse to select text. + - CTRL+Left/Right to word jump + - CTRL+Shift+Left/Right to select words + - CTRL+A our Double-Click to select all + - CTRL+X,CTRL+C,CTRL+V to use OS clipboard + - CTRL+Z,CTRL+Y to undo/redo + - ESCAPE to revert text to its original value + - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) + + + PROGRAMMER GUIDE + ================ + + - read the FAQ below this section! + - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. + - call and read ImGui::ShowTestWindow() for demo code demonstrating most features. + - see examples/ folder for standalone sample applications. Prefer reading examples/opengl2_example/ first as it is the simplest. + you may be able to grab and copy a ready made imgui_impl_*** file from the examples/. + - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). + + - getting started: + - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'. + - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory. + - every frame: + 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input' + 2/ call ImGui::NewFrame() as early as you can! + 3/ use any ImGui function you want between NewFrame() and Render() + 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure. + (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.) + - all rendering information are stored into command-lists until ImGui::Render() is called. + - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. + - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application. + - refer to the examples applications in the examples/ folder for instruction on how to setup your code. + - a typical application skeleton may be: + + // Application init + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = 1920.0f; + io.DisplaySize.y = 1280.0f; + io.IniFilename = "imgui.ini"; + io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data. + // TODO: Fill others settings of the io structure + + // Load texture atlas + // There is a default font so you don't need to care about choosing a font yet + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height); + // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system + // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID' + + // Application main loop + while (true) + { + // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.) + // TODO: fill all fields of IO structure and call NewFrame + ImGuiIO& io = ImGui::GetIO(); + io.DeltaTime = 1.0f/60.0f; + io.MousePos = mouse_pos; + io.MouseDown[0] = mouse_button_0; + io.MouseDown[1] = mouse_button_1; + io.KeysDown[i] = ... + + // 2) call NewFrame(), after this point you can use ImGui::* functions anytime + ImGui::NewFrame(); + + // 3) most of your application code here + MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); + MyGameRender(); // may use any ImGui functions + + // 4) render & swap video buffers + ImGui::Render(); + SwapBuffers(); + } + + - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your + inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information. + + + API BREAKING CHANGES + ==================== + + Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. + Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + Also read releases logs https://github.com/ocornut/imgui/releases for more details. + + - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild(). + - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. + - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. + - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. + - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. + If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. + However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. + This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color. + ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) + { + float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; + return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); + } + If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. + - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). + - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. + - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). + - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. + - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). + - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) + - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). + - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. + - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. + - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. + - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. + - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. + GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. + GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! + - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize + - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. + - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason + - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. + you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. + - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. + this necessary change will break your rendering function! the fix should be very easy. sorry for that :( + - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. + - the signature of the io.RenderDrawListsFn handler has changed! + ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) + became: + ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). + argument 'cmd_lists' -> 'draw_data->CmdLists' + argument 'cmd_lists_count' -> 'draw_data->CmdListsCount' + ImDrawList 'commands' -> 'CmdBuffer' + ImDrawList 'vtx_buffer' -> 'VtxBuffer' + ImDrawList n/a -> 'IdxBuffer' (new) + ImDrawCmd 'vtx_count' -> 'ElemCount' + ImDrawCmd 'clip_rect' -> 'ClipRect' + ImDrawCmd 'user_callback' -> 'UserCallback' + ImDrawCmd 'texture_id' -> 'TextureId' + - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. + - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! + - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! + - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. + - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). + - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. + - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence + - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! + - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). + - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). + - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. + - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened. + - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). + - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. + - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API + - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. + - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. + - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. + - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing + - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. + - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) + - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. + - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. + - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. + - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior + - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() + - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) + - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. + - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. + (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. + this sequence: + const void* png_data; + unsigned int png_size; + ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); + // + became: + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + // + io.Fonts->TexID = (your_texture_identifier); + you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. + it is now recommended that you sample the font texture with bilinear interpolation. + (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. + (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) + (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets + - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) + - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) + - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility + - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() + - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) + - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) + - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() + - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn + - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) + - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite + - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes + + + FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + ====================================== + + Q: How can I help? + A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help! + - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time. + + Q: How do I update to a newer version of ImGui? + A: Overwrite the following files: + imgui.cpp + imgui.h + imgui_demo.cpp + imgui_draw.cpp + imgui_internal.h + stb_rect_pack.h + stb_textedit.h + stb_truetype.h + Don't overwrite imconfig.h if you have made modification to your copy. + Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. If you have a problem with a function, search for its name + in the code, there will likely be a comment about it. Please report any issue to the GitHub page! + + + Q: What is ImTextureID and how do I display an image? + A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. + ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry! + It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. + At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. + Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. + (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) + To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. + ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. + It is your responsibility to get textures uploaded to your GPU. + + Q: I integrated ImGui in my engine and the text or lines are blurry.. + A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). + Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension. + + Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around.. + A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). + + Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) + A: Yes. A primer on the use of labels/IDs in ImGui.. + + - Elements that are not clickable, such as Text() items don't need an ID. + + - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget). + to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer. + + Button("OK"); // Label = "OK", ID = hash of "OK" + Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel" + + - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows + or in two different locations of a tree. + + - If you have a same ID twice in the same location, you'll have a conflict: + + Button("OK"); + Button("OK"); // ID collision! Both buttons will be treated as the same. + + Fear not! this is easy to solve and there are many ways to solve it! + + - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases. + use "##" to pass a complement to the ID that won't be visible to the end-user: + + Button("Play"); // Label = "Play", ID = hash of "Play" + Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above) + Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above) + + - If you want to completely hide the label, but still need an ID: + + Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!) + + - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. + For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously) + Use "###" to pass a label that isn't part of ID: + + Button("Hello###ID"; // Label = "Hello", ID = hash of "ID" + Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above) + + sprintf(buf, "My game (%f FPS)###MyGame"); + Begin(buf); // Variable label, ID = hash of "MyGame" + + - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window. + This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements. + You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack! + + for (int i = 0; i < 100; i++) + { + PushID(i); + Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj); + Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj->Name); + Button("Click"); // Label = "Click", ID = hash of string + "label" (unique) + PopID(); + } + + - More example showing that you can stack multiple prefixes into the ID stack: + + Button("Click"); // Label = "Click", ID = hash of "Click" + PushID("node"); + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + PushID(my_ptr); + Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click" + PopID(); + PopID(); + + - Tree nodes implicitly creates a scope for you by calling PushID(). + + Button("Click"); // Label = "Click", ID = hash of "Click" + if (TreeNode("node")) + { + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + TreePop(); + } + + - When working with trees, ID are used to preserve the open/close state of each tree node. + Depending on your use cases you may want to use strings, indices or pointers as ID. + e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change. + e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense! + + Q: How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application? + A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine. + When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application. + When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available. + ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow(). + (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'. + Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.) + + Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13) + A: Use the font atlas to load the TTF file you want: + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + + Q: How can I easily use icons in my application? + A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your strings. + Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions. + + Q: How can I load multiple fonts? + A: Use the font atlas to pack them into a single texture: + (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.) + + ImGuiIO& io = ImGui::GetIO(); + ImFont* font0 = io.Fonts->AddFontDefault(); + ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + // the first loaded font gets used by default + // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime + + // Options + ImFontConfig config; + config.OversampleH = 3; + config.OversampleV = 1; + config.GlyphExtraSpacing.x = 1.0f; + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); + + // Combine multiple fonts into one (e.g. for icon fonts) + ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; + ImFontConfig config; + config.MergeMode = true; + io.Fonts->AddFontDefault(); + io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs + + Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? + A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. + All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work. + In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. + You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code. + + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application + + As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that. + + Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) + A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha, + then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. + + - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code. + - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug" + - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings) + - tip: you can call Render() multiple times (e.g for VR renders). + - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui! + + + ISSUES & TODO-LIST + ================== + Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues + The list below consist mostly of notes of things to do before they are requested/discussed by users (at that point it usually happens on the github) + + - doc: add a proper documentation+regression testing system (#435) + - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. + - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis) (#690) + - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify. + - window: allow resizing of child windows (possibly given min/max for each axis?) + - window: background options for child windows, border option (disable rounding) + - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip) + - window: resizing from any sides? + mouse cursor directives for app. +!- window: begin with *p_open == false should return false. + - window: get size/pos helpers given names (see discussion in #249) + - window: a collapsed window can be stuck behind the main menu bar? + - window: when window is small, prioritize resize button over close button. + - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later. + - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. + - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. + - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). +!- scrolling: allow immediately effective change of scroll if we haven't appended items yet + - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) + - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. + - widgets: clean up widgets internal toward exposing everything. + - widgets: add disabled and read-only modes (#211) + - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. +!- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows). + - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes + - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? + - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile. + - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541) + - input text: expose CursorPos in char filter event (#816) + - input text: flag to disable live update of the user buffer (also applies to float/int text input) + - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text? + - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) + - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725) + - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc). + - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200) + - input text multi-line: line numbers? status bar? (follow up on #200) + - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725) + - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position. + - input number: optional range min/max for Input*() functions + - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) + - input number: use mouse wheel to step up/down + - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. + - button: provide a button that looks framed. + - text: proper alignment options + - image/image button: misalignment on padded/bordered button? + - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? + - layout: horizontal layout helper (#97) + - layout: horizontal flow until no space left (#404) + - layout: more generic alignment state (left/right/centered) for single items? + - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. + - layout: BeginGroup() needs a border option. + - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125) + - columns: add a conditional parameter to SetColumnOffset() (#513, #125) + - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125) + - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125) + - columns: user specify columns size (#513, #125) + - columns: flag to add horizontal separator above/below? + - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets) + - combo: sparse combo boxes (via function call?) / iterators + - combo: contents should extends to fit label if combo widget is small + - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203) + - listbox: multiple selection + - listbox: user may want to initial scroll to focus on the one selected value? + - listbox: keyboard navigation. + - listbox: scrolling should track modified selection. +!- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402) + - popups: add variant using global identifier similar to Begin/End (#402) + - popups: border options. richer api like BeginChild() perhaps? (#197) + - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred button" and may teleport when moving mouse + - menus: local shortcuts, global shortcuts (#456, #126) + - menus: icons + - menus: menubars: some sort of priority / effect of main menu-bar on desktop size? + - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely + - statusbar: add a per-window status bar helper similar to what menubar does. + - tabs (#261, #351) + - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) +!- color: the color helpers/typing is a mess and needs sorting out. + - color: add a better color picker (#346) + - node/graph editor (#306) + - pie menus patterns (#434) + - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479) + - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals) + - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) + - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) + - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) + - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() + - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). + - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. + - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign) + - slider & drag: int data passing through a float + - drag float: up/down axis + - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits) + - tree node / optimization: avoid formatting when clipped. + - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling. + - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings? + - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits? + - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer) + - tree node: tweak color scheme to distinguish headers from selected tree node (#581) + - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249) + - settings: write more decent code to allow saving/loading new fields + - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file + - style: add window shadows. + - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. + - style: color-box not always square? + - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc. + - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). + - style: global scale setting. + - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle + - text: simple markup language for color change? + - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. + - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance + - font: add support for kerning, probably optional. perhaps default to (32..128)^2 matrix ~ 36KB then hash fallback. + - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize) + - font: fix AddRemapChar() to work before font has been built. + - log: LogButtons() options for specifying depth and/or hiding depth slider + - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) + - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard) + - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. + - filters: set a current filter that tree node can automatically query to hide themselves + - filters: handle wildcards (with implicit leading/trailing *), regexps + - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) +!- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing + - keyboard: full keyboard navigation and focus. (#323) + - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) + - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) + - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71) + - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style). + - input: support track pad style scrolling & slider edit. + - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) + - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? + - misc: provide HoveredTime and ActivatedTime to ease the creation of animations. + - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438) + - style editor: color child window height expressed in multiple of line height. + - remote: make a system like RemoteImGui first-class citizen/project (#75) + - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?) + - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. + - examples: directx9: save/restore device state more thoroughly. + - examples: window minimize, maximize (#583) + - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335) + - optimization: use another hash function than crc32, e.g. FNV1a + - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? + - optimization: turn some the various stack vectors into statically-sized arrays + - optimization: better clipping for multi-component widgets +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#define IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_PLACEMENT_NEW +#include "imgui_internal.h" + +#include // toupper, isprint +#include // NULL, malloc, free, qsort, atoi +#include // vsnprintf, sscanf, printf +#include // INT_MIN, INT_MAX +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#endif + +// Clang warnings with -Weverything +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' // +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size +#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' +#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value +#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers +#endif + +//------------------------------------------------------------------------- +// Forward Declarations +//------------------------------------------------------------------------- + +static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL); + +static void PushMultiItemsWidths(int components, float w_full = 0.0f); +static float GetDraggedColumnOffset(int column_index); + +static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); + +static ImFont* GetDefaultFont(); +static void SetCurrentFont(ImFont* font); +static void SetCurrentWindow(ImGuiWindow* window); +static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond); +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond); +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond); +static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); +static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); +static inline bool IsWindowContentHoverable(ImGuiWindow* window); +static void ClearSetNextWindowData(); +static void CheckStacksSize(ImGuiWindow* window, bool write); +static void Scrollbar(ImGuiWindow* window, bool horizontal); + +static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); +static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window); +static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window); + +static ImGuiIniData* FindWindowSettings(const char* name); +static ImGuiIniData* AddWindowSettings(const char* name); +static void LoadIniSettingsFromDisk(const char* ini_filename); +static void SaveIniSettingsToDisk(const char* ini_filename); +static void MarkIniSettingsDirty(); + +static void PushColumnClipRect(int column_index = -1); +static ImRect GetVisibleRect(); + +static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags); +static void CloseInactivePopups(); +static void ClosePopupToLevel(int remaining); +static void ClosePopup(ImGuiID id); +static bool IsPopupOpen(ImGuiID id); +static ImGuiWindow* GetFrontMostModalRootWindow(); +static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); + +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size); +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size); +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2); +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format); + +//----------------------------------------------------------------------------- +// Platform dependent default implementations +//----------------------------------------------------------------------------- + +static const char* GetClipboardTextFn_DefaultImpl(void* user_data); +static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); + +//----------------------------------------------------------------------------- +// Context +//----------------------------------------------------------------------------- + +// Default font atlas storage . +// New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable. +static ImFontAtlas GImDefaultFontAtlas; + +// Default context storage + current context pointer. +// Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext() +// ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by: +// - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts) +// - or: Changing this variable to be TLS. You may #define GImGui in imconfig.h for further custom hackery. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +#ifndef GImGui +static ImGuiContext GImDefaultContext; +ImGuiContext* GImGui = &GImDefaultContext; +#endif + +//----------------------------------------------------------------------------- +// User facing structures +//----------------------------------------------------------------------------- + +ImGuiStyle::ImGuiStyle() +{ + Alpha = 1.0f; // Global alpha applies to everything in ImGui + WindowPadding = ImVec2(8,8); // Padding within a window + WindowMinSize = ImVec2(32,32); // Minimum window size + WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text + ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows + FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) + FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). + ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines + ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). + ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns + ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar + ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar + GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar + GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. + DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. + AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + + Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); + Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); + Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f); + Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f); + Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f); + Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input + Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); + Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); + Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); + Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); + Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); + Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); + Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); + Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); + Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); + Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f); + Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f); + Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); + Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); + Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f); + Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f); + Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); + Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); + Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); + Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f); + Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f); + Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); + Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); + Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f); + Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f); + Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f); + Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); + Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); + Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); + Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); +} + +ImGuiIO::ImGuiIO() +{ + // Most fields are initialized with zero + memset(this, 0, sizeof(*this)); + + DisplaySize = ImVec2(-1.0f, -1.0f); + DeltaTime = 1.0f/60.0f; + IniSavingRate = 5.0f; + IniFilename = "imgui.ini"; + LogFilename = "imgui_log.txt"; + Fonts = &GImDefaultFontAtlas; + FontGlobalScale = 1.0f; + FontDefault = NULL; + DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + MousePos = ImVec2(-1,-1); + MousePosPrev = ImVec2(-1,-1); + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) + MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) + KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < ImGuiKey_COUNT; i++) + KeyMap[i] = -1; + KeyRepeatDelay = 0.250f; + KeyRepeatRate = 0.050f; + UserData = NULL; + + // User functions + RenderDrawListsFn = NULL; + MemAllocFn = malloc; + MemFreeFn = free; + GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations + SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; + ClipboardUserData = NULL; + ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; + + // Set OS X style defaults based on __APPLE__ compile time flag +#ifdef __APPLE__ + OSXBehaviors = true; +#endif +} + +// Pass in translated ASCII characters for text input. +// - with glfw you can get those from the callback set in glfwSetCharCallback() +// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message +void ImGuiIO::AddInputCharacter(ImWchar c) +{ + const int n = ImStrlenW(InputCharacters); + if (n + 1 < IM_ARRAYSIZE(InputCharacters)) + { + InputCharacters[n] = c; + InputCharacters[n+1] = '\0'; + } +} + +void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) +{ + // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more + const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); + ImWchar wchars[wchars_buf_len]; + ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); + for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) + AddInputCharacter(wchars[i]); +} + +//----------------------------------------------------------------------------- +// HELPERS +//----------------------------------------------------------------------------- + +#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose +#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 + +// Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. +#ifdef _WIN32 +#define IM_NEWLINE "\r\n" +#else +#define IM_NEWLINE "\n" +#endif + +bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c) +{ + bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; + bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; + bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; + return ((b1 == b2) && (b2 == b3)); +} + +int ImStricmp(const char* str1, const char* str2) +{ + int d; + while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } + return d; +} + +int ImStrnicmp(const char* str1, const char* str2, int count) +{ + int d = 0; + while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } + return d; +} + +void ImStrncpy(char* dst, const char* src, int count) +{ + if (count < 1) return; + strncpy(dst, src, (size_t)count); + dst[count-1] = 0; +} + +char* ImStrdup(const char *str) +{ + size_t len = strlen(str) + 1; + void* buff = ImGui::MemAlloc(len); + return (char*)memcpy(buff, (const void*)str, len); +} + +int ImStrlenW(const ImWchar* str) +{ + int n = 0; + while (*str++) n++; + return n; +} + +const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line +{ + while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') + buf_mid_line--; + return buf_mid_line; +} + +const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) +{ + if (!needle_end) + needle_end = needle + strlen(needle); + + const char un0 = (char)toupper(*needle); + while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) + { + if (toupper(*haystack) == un0) + { + const char* b = needle + 1; + for (const char* a = haystack + 1; b < needle_end; a++, b++) + if (toupper(*a) != toupper(*b)) + break; + if (b == needle_end) + return haystack; + } + haystack++; + } + return NULL; +} + + +// MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). +// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. +int ImFormatString(char* buf, int buf_size, const char* fmt, ...) +{ + IM_ASSERT(buf_size > 0); + va_list args; + va_start(args, fmt); + int w = vsnprintf(buf, buf_size, fmt, args); + va_end(args); + if (w == -1 || w >= buf_size) + w = buf_size - 1; + buf[w] = 0; + return w; +} + +int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args) +{ + IM_ASSERT(buf_size > 0); + int w = vsnprintf(buf, buf_size, fmt, args); + if (w == -1 || w >= buf_size) + w = buf_size - 1; + buf[w] = 0; + return w; +} + +// Pass data_size==0 for zero-terminated strings +// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. +ImU32 ImHash(const void* data, int data_size, ImU32 seed) +{ + static ImU32 crc32_lut[256] = { 0 }; + if (!crc32_lut[1]) + { + const ImU32 polynomial = 0xEDB88320; + for (ImU32 i = 0; i < 256; i++) + { + ImU32 crc = i; + for (ImU32 j = 0; j < 8; j++) + crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); + crc32_lut[i] = crc; + } + } + + seed = ~seed; + ImU32 crc = seed; + const unsigned char* current = (const unsigned char*)data; + + if (data_size > 0) + { + // Known size + while (data_size--) + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + } + else + { + // Zero-terminated string + while (unsigned char c = *current++) + { + // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. + // Because this syntax is rarely used we are optimizing for the common case. + // - If we reach ### in the string we discard the hash so far and reset to the seed. + // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. + if (c == '#' && current[0] == '#' && current[1] == '#') + crc = seed; + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } + } + return ~crc; +} + +//----------------------------------------------------------------------------- +// ImText* helpers +//----------------------------------------------------------------------------- + +// Convert UTF-8 to 32-bits character, process single character input. +// Based on stb_from_utf8() from github.com/nothings/stb/ +// We handle UTF-8 decoding error by skipping forward. +int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) +{ + unsigned int c = (unsigned int)-1; + const unsigned char* str = (const unsigned char*)in_text; + if (!(*str & 0x80)) + { + c = (unsigned int)(*str++); + *out_char = c; + return 1; + } + if ((*str & 0xe0) == 0xc0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 2) return 1; + if (*str < 0xc2) return 2; + c = (unsigned int)((*str++ & 0x1f) << 6); + if ((*str & 0xc0) != 0x80) return 2; + c += (*str++ & 0x3f); + *out_char = c; + return 2; + } + if ((*str & 0xf0) == 0xe0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 3) return 1; + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; + if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x0f) << 12); + if ((*str & 0xc0) != 0x80) return 3; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 3; + c += (*str++ & 0x3f); + *out_char = c; + return 3; + } + if ((*str & 0xf8) == 0xf0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 4) return 1; + if (*str > 0xf4) return 4; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; + if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x07) << 18); + if ((*str & 0xc0) != 0x80) return 4; + c += (unsigned int)((*str++ & 0x3f) << 12); + if ((*str & 0xc0) != 0x80) return 4; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 4; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return 4; + *out_char = c; + return 4; + } + *out_char = 0; + return 0; +} + +int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) +{ + ImWchar* buf_out = buf; + ImWchar* buf_end = buf + buf_size; + while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c; + in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; + if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes + *buf_out++ = (ImWchar)c; + } + *buf_out = 0; + if (in_text_remaining) + *in_text_remaining = in_text; + return (int)(buf_out - buf); +} + +int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) +{ + int char_count = 0; + while ((!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c; + in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; + if (c < 0x10000) + char_count++; + } + return char_count; +} + +// Based on stb_to_utf8() from github.com/nothings/stb/ +static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +{ + if (c < 0x80) + { + buf[0] = (char)c; + return 1; + } + if (c < 0x800) + { + if (buf_size < 2) return 0; + buf[0] = (char)(0xc0 + (c >> 6)); + buf[1] = (char)(0x80 + (c & 0x3f)); + return 2; + } + if (c >= 0xdc00 && c < 0xe000) + { + return 0; + } + if (c >= 0xd800 && c < 0xdc00) + { + if (buf_size < 4) return 0; + buf[0] = (char)(0xf0 + (c >> 18)); + buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((c ) & 0x3f)); + return 4; + } + //else if (c < 0x10000) + { + if (buf_size < 3) return 0; + buf[0] = (char)(0xe0 + (c >> 12)); + buf[1] = (char)(0x80 + ((c>> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((c ) & 0x3f)); + return 3; + } +} + +static inline int ImTextCountUtf8BytesFromChar(unsigned int c) +{ + if (c < 0x80) return 1; + if (c < 0x800) return 2; + if (c >= 0xdc00 && c < 0xe000) return 0; + if (c >= 0xd800 && c < 0xdc00) return 4; + return 3; +} + +int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) +{ + char* buf_out = buf; + const char* buf_end = buf + buf_size; + while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c = (unsigned int)(*in_text++); + if (c < 0x80) + *buf_out++ = (char)c; + else + buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c); + } + *buf_out = 0; + return (int)(buf_out - buf); +} + +int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) +{ + int bytes_count = 0; + while ((!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c = (unsigned int)(*in_text++); + if (c < 0x80) + bytes_count++; + else + bytes_count += ImTextCountUtf8BytesFromChar(c); + } + return bytes_count; +} + +ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) +{ + float s = 1.0f/255.0f; + return ImVec4( + ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); +} + +ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) +{ + ImU32 out; + out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; + return out; +} + +ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) +{ + ImVec4 c = GImGui->Style.Colors[idx]; + c.w *= GImGui->Style.Alpha * alpha_mul; + return ColorConvertFloat4ToU32(c); +} + +ImU32 ImGui::GetColorU32(const ImVec4& col) +{ + ImVec4 c = col; + c.w *= GImGui->Style.Alpha; + return ColorConvertFloat4ToU32(c); +} + +// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 +// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv +void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) +{ + float K = 0.f; + if (g < b) + { + const float tmp = g; g = b; b = tmp; + K = -1.f; + } + if (r < g) + { + const float tmp = r; r = g; g = tmp; + K = -2.f / 6.f - K; + } + + const float chroma = r - (g < b ? g : b); + out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f)); + out_s = chroma / (r + 1e-20f); + out_v = r; +} + +// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 +// also http://en.wikipedia.org/wiki/HSL_and_HSV +void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) +{ + if (s == 0.0f) + { + // gray + out_r = out_g = out_b = v; + return; + } + + h = fmodf(h, 1.0f) / (60.0f/360.0f); + int i = (int)h; + float f = h - (float)i; + float p = v * (1.0f - s); + float q = v * (1.0f - s * f); + float t = v * (1.0f - s * (1.0f - f)); + + switch (i) + { + case 0: out_r = v; out_g = t; out_b = p; break; + case 1: out_r = q; out_g = v; out_b = p; break; + case 2: out_r = p; out_g = v; out_b = t; break; + case 3: out_r = p; out_g = q; out_b = v; break; + case 4: out_r = t; out_g = p; out_b = v; break; + case 5: default: out_r = v; out_g = p; out_b = q; break; + } +} + +FILE* ImFileOpen(const char* filename, const char* mode) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) + const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; + const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; + ImVector buf; + buf.resize(filename_wsize + mode_wsize); + ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); + ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); + return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); +#else + return fopen(filename, mode); +#endif +} + +// Load file content into memory +// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() +void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes) +{ + IM_ASSERT(filename && file_open_mode); + if (out_file_size) + *out_file_size = 0; + + FILE* f; + if ((f = ImFileOpen(filename, file_open_mode)) == NULL) + return NULL; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return NULL; + } + + int file_size = (int)file_size_signed; + void* file_data = ImGui::MemAlloc(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return NULL; + } + if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size) + { + fclose(f); + ImGui::MemFree(file_data); + return NULL; + } + if (padding_bytes > 0) + memset((void *)(((char*)file_data) + file_size), 0, padding_bytes); + + fclose(f); + if (out_file_size) + *out_file_size = file_size; + + return file_data; +} + +//----------------------------------------------------------------------------- +// ImGuiStorage +//----------------------------------------------------------------------------- + +// Helper: Key->value storage +void ImGuiStorage::Clear() +{ + Data.clear(); +} + +// std::lower_bound but without the bullshit +static ImVector::iterator LowerBound(ImVector& data, ImGuiID key) +{ + ImVector::iterator first = data.begin(); + ImVector::iterator last = data.end(); + int count = (int)(last - first); + while (count > 0) + { + int count2 = count / 2; + ImVector::iterator mid = first + count2; + if (mid->key < key) + { + first = ++mid; + count -= count2 + 1; + } + else + { + count = count2; + } + } + return first; +} + +int ImGuiStorage::GetInt(ImGuiID key, int default_val) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_i; +} + +bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const +{ + return GetInt(key, default_val ? 1 : 0) != 0; +} + +float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_f; +} + +void* ImGuiStorage::GetVoidPtr(ImGuiID key) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return NULL; + return it->val_p; +} + +// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. +int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_i; +} + +bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) +{ + return (bool*)GetIntRef(key, default_val ? 1 : 0); +} + +float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_f; +} + +void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_p; +} + +// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) +void ImGuiStorage::SetInt(ImGuiID key, int val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_i = val; +} + +void ImGuiStorage::SetBool(ImGuiID key, bool val) +{ + SetInt(key, val ? 1 : 0); +} + +void ImGuiStorage::SetFloat(ImGuiID key, float val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_f = val; +} + +void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_p = val; +} + +void ImGuiStorage::SetAllInt(int v) +{ + for (int i = 0; i < Data.Size; i++) + Data[i].val_i = v; +} + +//----------------------------------------------------------------------------- +// ImGuiTextFilter +//----------------------------------------------------------------------------- + +// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) +{ + if (default_filter) + { + ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); + Build(); + } + else + { + InputBuf[0] = 0; + CountGrep = 0; + } +} + +bool ImGuiTextFilter::Draw(const char* label, float width) +{ + if (width != 0.0f) + ImGui::PushItemWidth(width); + bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); + if (width != 0.0f) + ImGui::PopItemWidth(); + if (value_changed) + Build(); + return value_changed; +} + +void ImGuiTextFilter::TextRange::split(char separator, ImVector& out) +{ + out.resize(0); + const char* wb = b; + const char* we = wb; + while (we < e) + { + if (*we == separator) + { + out.push_back(TextRange(wb, we)); + wb = we + 1; + } + we++; + } + if (wb != we) + out.push_back(TextRange(wb, we)); +} + +void ImGuiTextFilter::Build() +{ + Filters.resize(0); + TextRange input_range(InputBuf, InputBuf+strlen(InputBuf)); + input_range.split(',', Filters); + + CountGrep = 0; + for (int i = 0; i != Filters.Size; i++) + { + Filters[i].trim_blanks(); + if (Filters[i].empty()) + continue; + if (Filters[i].front() != '-') + CountGrep += 1; + } +} + +bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const +{ + if (Filters.empty()) + return true; + + if (text == NULL) + text = ""; + + for (int i = 0; i != Filters.Size; i++) + { + const TextRange& f = Filters[i]; + if (f.empty()) + continue; + if (f.front() == '-') + { + // Subtract + if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL) + return false; + } + else + { + // Grep + if (ImStristr(text, text_end, f.begin(), f.end()) != NULL) + return true; + } + } + + // Implicit * grep + if (CountGrep == 0) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// ImGuiTextBuffer +//----------------------------------------------------------------------------- + +// On some platform vsnprintf() takes va_list by reference and modifies it. +// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. +#ifndef va_copy +#define va_copy(dest, src) (dest = src) +#endif + +// Helper: Text buffer for logging/accumulating text +void ImGuiTextBuffer::appendv(const char* fmt, va_list args) +{ + va_list args_copy; + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. + if (len <= 0) + return; + + const int write_off = Buf.Size; + const int needed_sz = write_off + len; + if (write_off + len >= Buf.Capacity) + { + int double_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); + } + + Buf.resize(needed_sz); + ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy); +} + +void ImGuiTextBuffer::append(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + appendv(fmt, args); + va_end(args); +} + +//----------------------------------------------------------------------------- +// ImGuiSimpleColumns +//----------------------------------------------------------------------------- + +ImGuiSimpleColumns::ImGuiSimpleColumns() +{ + Count = 0; + Spacing = Width = NextWidth = 0.0f; + memset(Pos, 0, sizeof(Pos)); + memset(NextWidths, 0, sizeof(NextWidths)); +} + +void ImGuiSimpleColumns::Update(int count, float spacing, bool clear) +{ + IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); + Count = count; + Width = NextWidth = 0.0f; + Spacing = spacing; + if (clear) memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < Count; i++) + { + if (i > 0 && NextWidths[i] > 0.0f) + Width += Spacing; + Pos[i] = (float)(int)Width; + Width += NextWidths[i]; + NextWidths[i] = 0.0f; + } +} + +float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +{ + NextWidth = 0.0f; + NextWidths[0] = ImMax(NextWidths[0], w0); + NextWidths[1] = ImMax(NextWidths[1], w1); + NextWidths[2] = ImMax(NextWidths[2], w2); + for (int i = 0; i < 3; i++) + NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); + return ImMax(Width, NextWidth); +} + +float ImGuiSimpleColumns::CalcExtraSpace(float avail_w) +{ + return ImMax(0.0f, avail_w - Width); +} + +//----------------------------------------------------------------------------- +// ImGuiListClipper +//----------------------------------------------------------------------------- + +static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) +{ + // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. + // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions? + ImGui::SetCursorPosY(pos_y); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. + window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. + if (window->DC.ColumnsCount > 1) + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly +} + +// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 +// Use case B: Begin() called from constructor with items_height>0 +// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. +void ImGuiListClipper::Begin(int count, float items_height) +{ + StartPosY = ImGui::GetCursorPosY(); + ItemsHeight = items_height; + ItemsCount = count; + StepNo = 0; + DisplayEnd = DisplayStart = -1; + if (ItemsHeight > 0.0f) + { + ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display + if (DisplayStart > 0) + SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor + StepNo = 2; + } +} + +void ImGuiListClipper::End() +{ + if (ItemsCount < 0) + return; + // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. + if (ItemsCount < INT_MAX) + SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor + ItemsCount = -1; + StepNo = 3; +} + +bool ImGuiListClipper::Step() +{ + if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems) + { + ItemsCount = -1; + return false; + } + if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. + { + DisplayStart = 0; + DisplayEnd = 1; + StartPosY = ImGui::GetCursorPosY(); + StepNo = 1; + return true; + } + if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. + { + if (ItemsCount == 1) { ItemsCount = -1; return false; } + float items_height = ImGui::GetCursorPosY() - StartPosY; + IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically + Begin(ItemsCount-1, items_height); + DisplayStart++; + DisplayEnd++; + StepNo = 3; + return true; + } + if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3. + { + IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0); + StepNo = 3; + return true; + } + if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. + End(); + return false; +} + +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- + +ImGuiWindow::ImGuiWindow(const char* name) +{ + Name = ImStrdup(name); + ID = ImHash(name, 0); + IDStack.push_back(ID); + MoveId = GetID("#MOVE"); + + Flags = 0; + IndexWithinParent = 0; + PosFloat = Pos = ImVec2(0.0f, 0.0f); + Size = SizeFull = ImVec2(0.0f, 0.0f); + SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); + WindowPadding = ImVec2(0.0f, 0.0f); + Scroll = ImVec2(0.0f, 0.0f); + ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); + ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); + ScrollbarX = ScrollbarY = false; + ScrollbarSizes = ImVec2(0.0f, 0.0f); + BorderSize = 0.0f; + Active = WasActive = false; + Accessed = false; + Collapsed = false; + SkipItems = false; + BeginCount = 0; + PopupId = 0; + AutoFitFramesX = AutoFitFramesY = -1; + AutoFitOnlyGrows = false; + AutoPosLastDirection = -1; + HiddenFrames = 0; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing; + SetWindowPosCenterWanted = false; + + LastFrameActive = -1; + ItemWidthDefault = 0.0f; + FontWindowScale = 1.0f; + + DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); + IM_PLACEMENT_NEW(DrawList) ImDrawList(); + DrawList->_OwnerName = Name; + RootWindow = NULL; + RootNonPopupWindow = NULL; + ParentWindow = NULL; + + FocusIdxAllCounter = FocusIdxTabCounter = -1; + FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; + FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; +} + +ImGuiWindow::~ImGuiWindow() +{ + DrawList->~ImDrawList(); + ImGui::MemFree(DrawList); + DrawList = NULL; + ImGui::MemFree(Name); + Name = NULL; +} + +ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetID(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(&ptr, sizeof(void*), seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); +} + +//----------------------------------------------------------------------------- +// Internal API exposed in imgui_internal.h +//----------------------------------------------------------------------------- + +static void SetCurrentWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow = window; + if (window) + g.FontSize = window->CalcFontSize(); +} + +ImGuiWindow* ImGui::GetParentWindow() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindowStack.Size >= 2); + return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2]; +} + +void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) +{ + ImGuiContext& g = *GImGui; + g.ActiveId = id; + g.ActiveIdAllowOverlap = false; + g.ActiveIdIsJustActivated = true; + if (id) + g.ActiveIdIsAlive = true; + g.ActiveIdWindow = window; +} + +void ImGui::SetHoveredID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.HoveredId = id; + g.HoveredIdAllowOverlap = false; +} + +void ImGui::KeepAliveID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + g.ActiveIdIsAlive = true; +} + +// Advance cursor given item size for layout. +void ImGui::ItemSize(const ImVec2& size, float text_offset_y) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + // Always align ourselves on pixel boundaries + ImGuiContext& g = *GImGui; + const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); + const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); + window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); + window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + + //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug + + window->DC.PrevLineHeight = line_height; + window->DC.PrevLineTextBaseOffset = text_base_offset; + window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; +} + +void ImGui::ItemSize(const ImRect& bb, float text_offset_y) +{ + ItemSize(bb.GetSize(), text_offset_y); +} + +// Declare item bounding box for clipping and interaction. +// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface +// declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). +bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.LastItemId = id ? *id : 0; + window->DC.LastItemRect = bb; + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; + if (IsClippedEx(bb, id, false)) + return false; + + // This is a sensible default, but widgets are free to override it after calling ItemAdd() + ImGuiContext& g = *GImGui; + if (IsMouseHoveringRect(bb.Min, bb.Max)) + { + // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background) + // So that clicking on items with no active id such as Text() still returns true with IsItemHovered() + window->DC.LastItemHoveredRect = true; + if (g.HoveredRootWindow == window->RootWindow) + if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId)) + if (IsWindowContentHoverable(window)) + window->DC.LastItemHoveredAndUsable = true; + } + + return true; +} + +bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + + if (!bb.Overlaps(window->ClipRect)) + if (!id || *id != GImGui->ActiveId) + if (clip_even_when_logged || !g.LogEnabled) + return true; + return false; +} + +// NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic. +bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs) +{ + ImGuiContext& g = *GImGui; + if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap) + { + ImGuiWindow* window = GetCurrentWindowRead(); + if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow)) + if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max)) + if (IsWindowContentHoverable(g.HoveredRootWindow)) + return true; + } + return false; +} + +bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop) +{ + ImGuiContext& g = *GImGui; + + const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus; + window->FocusIdxAllCounter++; + if (allow_keyboard_focus) + window->FocusIdxTabCounter++; + + // Process keyboard input at this point: TAB, Shift-TAB switch focus + // We can always TAB out of a widget that doesn't allow tabbing in. + if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab)) + { + // Modulo on index will be applied at the end of frame once we've got the total counter of items. + window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); + } + + if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) + return true; + + if (allow_keyboard_focus) + if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + return true; + + return false; +} + +void ImGui::FocusableItemUnregister(ImGuiWindow* window) +{ + window->FocusIdxAllCounter--; + window->FocusIdxTabCounter--; +} + +ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) +{ + ImGuiContext& g = *GImGui; + ImVec2 content_max; + if (size.x < 0.0f || size.y < 0.0f) + content_max = g.CurrentWindow->Pos + GetContentRegionMax(); + if (size.x <= 0.0f) + size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x; + if (size.y <= 0.0f) + size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y; + return size; +} + +float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) +{ + if (wrap_pos_x < 0.0f) + return 0.0f; + + ImGuiWindow* window = GetCurrentWindowRead(); + if (wrap_pos_x == 0.0f) + wrap_pos_x = GetContentRegionMax().x + window->Pos.x; + else if (wrap_pos_x > 0.0f) + wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space + + return ImMax(wrap_pos_x - pos.x, 1.0f); +} + +//----------------------------------------------------------------------------- + +void* ImGui::MemAlloc(size_t sz) +{ + GImGui->IO.MetricsAllocs++; + return GImGui->IO.MemAllocFn(sz); +} + +void ImGui::MemFree(void* ptr) +{ + if (ptr) GImGui->IO.MetricsAllocs--; + return GImGui->IO.MemFreeFn(ptr); +} + +const char* ImGui::GetClipboardText() +{ + return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : ""; +} + +void ImGui::SetClipboardText(const char* text) +{ + if (GImGui->IO.SetClipboardTextFn) + GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text); +} + +const char* ImGui::GetVersion() +{ + return IMGUI_VERSION; +} + +// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself +// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module +ImGuiContext* ImGui::GetCurrentContext() +{ + return GImGui; +} + +void ImGui::SetCurrentContext(ImGuiContext* ctx) +{ +#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC + IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. +#else + GImGui = ctx; +#endif +} + +ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*)) +{ + if (!malloc_fn) malloc_fn = malloc; + ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext)); + IM_PLACEMENT_NEW(ctx) ImGuiContext(); + ctx->IO.MemAllocFn = malloc_fn; + ctx->IO.MemFreeFn = free_fn ? free_fn : free; + return ctx; +} + +void ImGui::DestroyContext(ImGuiContext* ctx) +{ + void (*free_fn)(void*) = ctx->IO.MemFreeFn; + ctx->~ImGuiContext(); + free_fn(ctx); + if (GImGui == ctx) + SetCurrentContext(NULL); +} + +ImGuiIO& ImGui::GetIO() +{ + return GImGui->IO; +} + +ImGuiStyle& ImGui::GetStyle() +{ + return GImGui->Style; +} + +// Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame() +ImDrawData* ImGui::GetDrawData() +{ + return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL; +} + +float ImGui::GetTime() +{ + return GImGui->Time; +} + +int ImGui::GetFrameCount() +{ + return GImGui->FrameCount; +} + +void ImGui::NewFrame() +{ + ImGuiContext& g = *GImGui; + + // Check user data + IM_ASSERT(g.IO.DeltaTime >= 0.0f); // Need a positive DeltaTime (zero is tolerated but will cause some timing issues) + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f); + IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting + + if (!g.Initialized) + { + // Initialize on first frame + g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer)); + IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer(); + + IM_ASSERT(g.Settings.empty()); + LoadIniSettingsFromDisk(g.IO.IniFilename); + g.Initialized = true; + } + + SetCurrentFont(GetDefaultFont()); + IM_ASSERT(g.Font->IsLoaded()); + + g.Time += g.IO.DeltaTime; + g.FrameCount += 1; + g.Tooltip[0] = '\0'; + g.OverlayDrawList.Clear(); + g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); + g.OverlayDrawList.PushClipRectFullScreen(); + + // Mark rendering data as invalid to prevent user who may have a handle on it to use it + g.RenderDrawData.Valid = false; + g.RenderDrawData.CmdLists = NULL; + g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0; + + // Update inputs state + if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) + g.IO.MousePos = ImVec2(-9999.0f, -9999.0f); + if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta + g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + else + g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + g.IO.MousePosPrev = g.IO.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + { + g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; + g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; + g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; + g.IO.MouseDoubleClicked[i] = false; + if (g.IO.MouseClicked[i]) + { + if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) + { + if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + g.IO.MouseDoubleClicked[i] = true; + g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click + } + else + { + g.IO.MouseClickedTime[i] = g.Time; + } + g.IO.MouseClickedPos[i] = g.IO.MousePos; + g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (g.IO.MouseDown[i]) + { + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); + } + } + memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) + g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + + // Calculate frame-rate for the user, as a purely luxurious feature + g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; + g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); + g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); + + // Clear reference to active widget if the widget isn't alive anymore + g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredId = 0; + g.HoveredIdAllowOverlap = false; + if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) + SetActiveID(0); + g.ActiveIdPreviousFrame = g.ActiveId; + g.ActiveIdIsAlive = false; + g.ActiveIdIsJustActivated = false; + + // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. + if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId) + { + KeepAliveID(g.MovedWindowMoveId); + IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow); + IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId); + if (g.IO.MouseDown[0]) + { + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove)) + { + g.MovedWindow->PosFloat += g.IO.MouseDelta; + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings) && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) + MarkIniSettingsDirty(); + } + FocusWindow(g.MovedWindow); + } + else + { + SetActiveID(0); + g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } + } + else + { + g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } + + // Delay saving settings so we don't spam disk too much + if (g.SettingsDirtyTimer > 0.0f) + { + g.SettingsDirtyTimer -= g.IO.DeltaTime; + if (g.SettingsDirtyTimer <= 0.0f) + SaveIniSettingsToDisk(g.IO.IniFilename); + } + + // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow + g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false); + if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow)) + g.HoveredRootWindow = g.HoveredWindow->RootWindow; + else + g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true); + + if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow()) + { + g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); + ImGuiWindow* window = g.HoveredRootWindow; + while (window && window != modal_window) + window = window->ParentWindow; + if (!window) + g.HoveredRootWindow = g.HoveredWindow = NULL; + } + else + { + g.ModalWindowDarkeningRatio = 0.0f; + } + + // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application. + // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership. + int mouse_earliest_button_down = -1; + bool mouse_any_down = false; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + { + if (g.IO.MouseClicked[i]) + g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); + mouse_any_down |= g.IO.MouseDown[i]; + if (g.IO.MouseDown[i]) + if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i]) + mouse_earliest_button_down = i; + } + bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + if (g.CaptureMouseNextFrame != -1) + g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0); + else + g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty()); + g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0); + g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId); + g.MouseCursor = ImGuiMouseCursor_Arrow; + g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1; + g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + + // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. + if (!mouse_avail_to_imgui) + g.HoveredWindow = g.HoveredRootWindow = NULL; + + // Scale & Scrolling + if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed) + { + ImGuiWindow* window = g.HoveredWindow; + if (g.IO.KeyCtrl) + { + if (g.IO.FontAllowUserScaling) + { + // Zoom / Scale window + float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + window->Pos += offset; + window->PosFloat += offset; + window->Size *= scale; + window->SizeFull *= scale; + } + } + else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) + { + // Scroll + const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5; + SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines); + } + } + + // Pressing TAB activate widget focus + // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false)) + g.FocusedWindow->FocusIdxTabRequestNext = 0; + + // Mark all windows as not visible + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + window->WasActive = window->Active; + window->Active = false; + window->Accessed = false; + } + + // Closing the focused window restore focus to the first active root window in descending z-order + if (g.FocusedWindow && !g.FocusedWindow->WasActive) + for (int i = g.Windows.Size-1; i >= 0; i--) + if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + { + FocusWindow(g.Windows[i]); + break; + } + + // No window should be open at the beginning of the frame. + // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. + g.CurrentWindowStack.resize(0); + g.CurrentPopupStack.resize(0); + CloseInactivePopups(); + + // Create implicit window - we will only render it if the user has added something to it. + ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Debug"); +} + +// NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations. +void ImGui::Shutdown() +{ + ImGuiContext& g = *GImGui; + + // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) + if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky. + g.IO.Fonts->Clear(); + + // Cleanup of other data are conditional on actually having used ImGui. + if (!g.Initialized) + return; + + SaveIniSettingsToDisk(g.IO.IniFilename); + + for (int i = 0; i < g.Windows.Size; i++) + { + g.Windows[i]->~ImGuiWindow(); + ImGui::MemFree(g.Windows[i]); + } + g.Windows.clear(); + g.WindowsSortBuffer.clear(); + g.CurrentWindow = NULL; + g.CurrentWindowStack.clear(); + g.FocusedWindow = NULL; + g.HoveredWindow = NULL; + g.HoveredRootWindow = NULL; + g.ActiveIdWindow = NULL; + g.MovedWindow = NULL; + for (int i = 0; i < g.Settings.Size; i++) + ImGui::MemFree(g.Settings[i].Name); + g.Settings.clear(); + g.ColorModifiers.clear(); + g.StyleModifiers.clear(); + g.FontStack.clear(); + g.OpenPopupStack.clear(); + g.CurrentPopupStack.clear(); + g.SetNextWindowSizeConstraintCallback = NULL; + g.SetNextWindowSizeConstraintCallbackUserData = NULL; + for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].clear(); + g.OverlayDrawList.ClearFreeMemory(); + g.ColorEditModeStorage.Clear(); + if (g.PrivateClipboard) + { + ImGui::MemFree(g.PrivateClipboard); + g.PrivateClipboard = NULL; + } + g.InputTextState.Text.clear(); + g.InputTextState.InitialText.clear(); + g.InputTextState.TempTextBuffer.clear(); + + if (g.LogFile && g.LogFile != stdout) + { + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard) + { + g.LogClipboard->~ImGuiTextBuffer(); + ImGui::MemFree(g.LogClipboard); + } + + g.Initialized = false; +} + +static ImGuiIniData* FindWindowSettings(const char* name) +{ + ImGuiContext& g = *GImGui; + ImGuiID id = ImHash(name, 0); + for (int i = 0; i != g.Settings.Size; i++) + { + ImGuiIniData* ini = &g.Settings[i]; + if (ini->Id == id) + return ini; + } + return NULL; +} + +static ImGuiIniData* AddWindowSettings(const char* name) +{ + GImGui->Settings.resize(GImGui->Settings.Size + 1); + ImGuiIniData* ini = &GImGui->Settings.back(); + ini->Name = ImStrdup(name); + ini->Id = ImHash(name, 0); + ini->Collapsed = false; + ini->Pos = ImVec2(FLT_MAX,FLT_MAX); + ini->Size = ImVec2(0,0); + return ini; +} + +// Zero-tolerance, poor-man .ini parsing +// FIXME: Write something less rubbish +static void LoadIniSettingsFromDisk(const char* ini_filename) +{ + ImGuiContext& g = *GImGui; + if (!ini_filename) + return; + + int file_size; + char* file_data = (char*)ImLoadFileToMemory(ini_filename, "rb", &file_size, 1); + if (!file_data) + return; + + ImGuiIniData* settings = NULL; + const char* buf_end = file_data + file_size; + for (const char* line_start = file_data; line_start < buf_end; ) + { + const char* line_end = line_start; + while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') + line_end++; + + if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']') + { + char name[64]; + ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1); + settings = FindWindowSettings(name); + if (!settings) + settings = AddWindowSettings(name); + } + else if (settings) + { + float x, y; + int i; + if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2) + settings->Pos = ImVec2(x, y); + else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2) + settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize); + else if (sscanf(line_start, "Collapsed=%d", &i) == 1) + settings->Collapsed = (i != 0); + } + + line_start = line_end+1; + } + + ImGui::MemFree(file_data); +} + +static void SaveIniSettingsToDisk(const char* ini_filename) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + if (!ini_filename) + return; + + // Gather data from windows that were active during this session + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) + continue; + ImGuiIniData* settings = FindWindowSettings(window->Name); + settings->Pos = window->Pos; + settings->Size = window->SizeFull; + settings->Collapsed = window->Collapsed; + } + + // Write .ini file + // If a window wasn't opened in this session we preserve its settings + FILE* f = ImFileOpen(ini_filename, "wt"); + if (!f) + return; + for (int i = 0; i != g.Settings.Size; i++) + { + const ImGuiIniData* settings = &g.Settings[i]; + if (settings->Pos.x == FLT_MAX) + continue; + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + fprintf(f, "[%s]\n", name); + fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + fprintf(f, "Collapsed=%d\n", settings->Collapsed); + fprintf(f, "\n"); + } + + fclose(f); +} + +static void MarkIniSettingsDirty() +{ + ImGuiContext& g = *GImGui; + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +// FIXME: Add a more explicit sort order in the window structure. +static int ChildWindowComparer(const void* lhs, const void* rhs) +{ + const ImGuiWindow* a = *(const ImGuiWindow**)lhs; + const ImGuiWindow* b = *(const ImGuiWindow**)rhs; + if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox)) + return d; + return (a->IndexWithinParent - b->IndexWithinParent); +} + +static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window) +{ + out_sorted_windows.push_back(window); + if (window->Active) + { + int count = window->DC.ChildWindows.Size; + if (count > 1) + qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + for (int i = 0; i < count; i++) + { + ImGuiWindow* child = window->DC.ChildWindows[i]; + if (child->Active) + AddWindowToSortedBuffer(out_sorted_windows, child); + } + } +} + +static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list) +{ + if (draw_list->CmdBuffer.empty()) + return; + + // Remove trailing command if unused + ImDrawCmd& last_cmd = draw_list->CmdBuffer.back(); + if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL) + { + draw_list->CmdBuffer.pop_back(); + if (draw_list->CmdBuffer.empty()) + return; + } + + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices) + // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly. + IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8))); // Too many vertices in same ImDrawList. See comment above. + + out_render_list.push_back(draw_list); + GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size; + GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size; +} + +static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window) +{ + AddDrawListToRenderList(out_render_list, window->DrawList); + for (int i = 0; i < window->DC.ChildWindows.Size; i++) + { + ImGuiWindow* child = window->DC.ChildWindows[i]; + if (!child->Active) // clipped children may have been marked not active + continue; + if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0) + continue; + AddWindowToRenderList(out_render_list, child); + } +} + +// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result. +void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); + window->ClipRect = window->DrawList->_ClipRectStack.back(); +} + +void ImGui::PopClipRect() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->PopClipRect(); + window->ClipRect = window->DrawList->_ClipRectStack.back(); +} + +// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. +void ImGui::EndFrame() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.FrameCountEnded != g.FrameCount); // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again + + // Render tooltip + if (g.Tooltip[0]) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(g.Tooltip); + ImGui::EndTooltip(); + } + + // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) + if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f) + { + g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y); + g.OsImePosSet = g.OsImePosRequest; + } + + // Hide implicit "Debug" window if it hasn't been used + IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls + if (g.CurrentWindow && !g.CurrentWindow->Accessed) + g.CurrentWindow->Active = false; + ImGui::End(); + + // Click to focus window and start moving (after we're done with all our widgets) + if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0]) + { + if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear + { + if (g.HoveredRootWindow != NULL) + { + FocusWindow(g.HoveredWindow); + if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove)) + { + g.MovedWindow = g.HoveredWindow; + g.MovedWindowMoveId = g.HoveredRootWindow->MoveId; + SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow); + } + } + else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) + { + // Clicking on void disable focus + FocusWindow(NULL); + } + } + } + + // Sort the window list so that all child windows are after their parent + // We cannot do that on FocusWindow() because childs may not exist yet + g.WindowsSortBuffer.resize(0); + g.WindowsSortBuffer.reserve(g.Windows.Size); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it + continue; + AddWindowToSortedBuffer(g.WindowsSortBuffer, window); + } + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + g.Windows.swap(g.WindowsSortBuffer); + + // Clear Input data for next frame + g.IO.MouseWheel = 0.0f; + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + + g.FrameCountEnded = g.FrameCount; +} + +void ImGui::Render() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + + if (g.FrameCountEnded != g.FrameCount) + ImGui::EndFrame(); + g.FrameCountRendered = g.FrameCount; + + // Skip render altogether if alpha is 0.0 + // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false. + if (g.Style.Alpha > 0.0f) + { + // Gather windows to render + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; + for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].resize(0); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0) + { + // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, .. + g.IO.MetricsActiveWindows++; + if (window->Flags & ImGuiWindowFlags_Popup) + AddWindowToRenderList(g.RenderDrawLists[1], window); + else if (window->Flags & ImGuiWindowFlags_Tooltip) + AddWindowToRenderList(g.RenderDrawLists[2], window); + else + AddWindowToRenderList(g.RenderDrawLists[0], window); + } + } + + // Flatten layers + int n = g.RenderDrawLists[0].Size; + int flattened_size = n; + for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + flattened_size += g.RenderDrawLists[i].Size; + g.RenderDrawLists[0].resize(flattened_size); + for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + { + ImVector& layer = g.RenderDrawLists[i]; + if (layer.empty()) + continue; + memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); + n += layer.Size; + } + + // Draw software mouse cursor if requested + if (g.IO.MouseDrawCursor) + { + const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor]; + const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset; + const ImVec2 size = cursor_data.Size; + const ImTextureID tex_id = g.IO.Fonts->TexID; + g.OverlayDrawList.PushTextureID(tex_id); + g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255)); // Black border + g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill + g.OverlayDrawList.PopTextureID(); + } + if (!g.OverlayDrawList.VtxBuffer.empty()) + AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList); + + // Setup draw data + g.RenderDrawData.Valid = true; + g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL; + g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size; + g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices; + g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices; + + // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() + if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(&g.RenderDrawData); + } +} + +const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) +{ + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; + + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + return text_display_end; +} + +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + { + vfprintf(g.LogFile, fmt, args); + } + else + { + g.LogClipboard->appendv(fmt, args); + } + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding +static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindowRead(); + + if (!text_end) + text_end = ImGui::FindRenderedTextEnd(text, text_end); + + const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1; + window->DC.LogLinePosY = ref_pos.y; + + const char* text_remaining = text; + if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogStartDepth = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + for (;;) + { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + const char* line_end = text_remaining; + while (line_end < text_end) + if (*line_end == '\n') + break; + else + line_end++; + if (line_end >= text_end) + line_end = NULL; + + const bool is_first_line = (text == text_remaining); + bool is_last_line = false; + if (line_end == NULL) + { + is_last_line = true; + line_end = text_end; + } + if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) + { + const int char_count = (int)(line_end - text_remaining); + if (log_new_line || !is_first_line) + ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); + else + ImGui::LogText(" %.*s", char_count, text_remaining); + } + + if (is_last_line) + break; + text_remaining = line_end + 1; + } +} + +// Internal ImGui functions to render text +// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() +void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Hide anything after a '##' string + const char* text_display_end; + if (hide_text_after_hash) + { + text_display_end = FindRenderedTextEnd(text, text_end); + } + else + { + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + text_display_end = text_end; + } + + const int text_len = (int)(text_display_end - text); + if (text_len > 0) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + if (g.LogEnabled) + LogRenderedText(pos, text, text_display_end); + } +} + +void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + const int text_len = (int)(text_end - text); + if (text_len > 0) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogRenderedText(pos, text, text_end); + } +} + +// Default clip_rect uses (pos_min,pos_max) +// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Perform CPU side clipping for single clipped element to avoid using scissor state + ImVec2 pos = pos_min; + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); + + const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; + const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; + bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min + need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); + + // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. + if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); + if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); + + // Render + if (need_clipping) + { + ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } + else + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } + if (g.LogEnabled) + LogRenderedText(pos, text, text_display_end); +} + +// Render a rectangle shaped with optional rounding and borders +void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) +{ + ImGuiWindow* window = GetCurrentWindow(); + + window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); + if (border && (window->Flags & ImGuiWindowFlags_ShowBorders)) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding); + } +} + +// Render a triangle to denote expanded/collapsed state +void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + const float h = g.FontSize * 1.00f; + const float r = h * 0.40f * scale; + ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale); + + ImVec2 a, b, c; + if (is_open) + { + center.y -= r*0.25f; + a = center + ImVec2(0,1)*r; + b = center + ImVec2(-0.866f,-0.5f)*r; + c = center + ImVec2(0.866f,-0.5f)*r; + } + else + { + a = center + ImVec2(1,0)*r; + b = center + ImVec2(-0.500f,0.866f)*r; + c = center + ImVec2(-0.500f,-0.866f)*r; + } + + window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text)); +} + +void ImGui::RenderBullet(ImVec2 pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); +} + +void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + ImVec2 a, b, c; + float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f); + float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f); + a.x = pos.x + 0.5f + start_x; + b.x = a.x + rem_third; + c.x = a.x + rem_third * 3.0f; + b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y); + a.y = b.y - rem_third; + c.y = b.y - rem_third * 2.0f; + + window->DrawList->PathLineTo(a); + window->DrawList->PathLineTo(b); + window->DrawList->PathLineTo(c); + window->DrawList->PathStroke(col, false); +} + +// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. +// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) +ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) +{ + ImGuiContext& g = *GImGui; + + const char* text_display_end; + if (hide_text_after_double_hash) + text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string + else + text_display_end = text_end; + + ImFont* font = g.Font; + const float font_size = g.FontSize; + if (text == text_display_end) + return ImVec2(0.0f, font_size); + ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); + + // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field) + const float font_scale = font_size / font->FontSize; + const float character_spacing_x = 1.0f * font_scale; + if (text_size.x > 0.0f) + text_size.x -= character_spacing_x; + text_size.x = (float)(int)(text_size.x + 0.95f); + + return text_size; +} + +// Helper to calculate coarse clipping of large list of evenly sized items. +// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. +// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + *out_items_display_start = 0; + *out_items_display_end = items_count; + return; + } + if (window->SkipItems) + { + *out_items_display_start = *out_items_display_end = 0; + return; + } + + const ImVec2 pos = window->DC.CursorPos; + int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); + int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); + start = ImClamp(start, 0, items_count); + end = ImClamp(end + 1, start, items_count); + *out_items_display_start = start; + *out_items_display_end = end; +} + +// Find window given position, search front-to-back +// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. +static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) +{ + ImGuiContext& g = *GImGui; + for (int i = g.Windows.Size-1; i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (!window->Active) + continue; + if (window->Flags & ImGuiWindowFlags_NoInputs) + continue; + if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0) + continue; + + // Using the clipped AABB so a child window will typically be clipped by its parent. + ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); + if (bb.Contains(pos)) + return window; + } + return NULL; +} + +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + + // Clip + ImRect rect_clipped(r_min, r_max); + if (clip) + rect_clipped.Clip(window->ClipRect); + + // Expand for touch input + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + return rect_for_touch.Contains(g.IO.MousePos); +} + +bool ImGui::IsMouseHoveringWindow() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow == g.CurrentWindow; +} + +bool ImGui::IsMouseHoveringAnyWindow() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow != NULL; +} + +bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos) +{ + return FindHoveredWindow(pos, false) != NULL; +} + +static bool IsKeyPressedMap(ImGuiKey key, bool repeat) +{ + const int key_index = GImGui->IO.KeyMap[key]; + return ImGui::IsKeyPressed(key_index, repeat); +} + +int ImGui::GetKeyIndex(ImGuiKey key) +{ + IM_ASSERT(key >= 0 && key < ImGuiKey_COUNT); + return GImGui->IO.KeyMap[key]; +} + +bool ImGui::IsKeyDown(int key_index) +{ + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown)); + return GImGui->IO.KeysDown[key_index]; +} + +bool ImGui::IsKeyPressed(int key_index, bool repeat) +{ + ImGuiContext& g = *GImGui; + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + const float t = g.IO.KeysDownDuration[key_index]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) + { + float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; + if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + return true; + } + return false; +} + +bool ImGui::IsKeyReleased(int key_index) +{ + ImGuiContext& g = *GImGui; + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + if (g.IO.KeysDownDurationPrev[key_index] >= 0.0f && !g.IO.KeysDown[key_index]) + return true; + return false; +} + +bool ImGui::IsMouseDown(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button]; +} + +bool ImGui::IsMouseClicked(int button, bool repeat) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float t = g.IO.MouseDownDuration[button]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) + { + float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; + if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + return true; + } + + return false; +} + +bool ImGui::IsMouseReleased(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseReleased[button]; +} + +bool ImGui::IsMouseDoubleClicked(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDoubleClicked[button]; +} + +bool ImGui::IsMouseDragging(int button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (!g.IO.MouseDown[button]) + return false; + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; +} + +ImVec2 ImGui::GetMousePos() +{ + return GImGui->IO.MousePos; +} + +// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! +ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() +{ + ImGuiContext& g = *GImGui; + if (g.CurrentPopupStack.Size > 0) + return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen; + return g.IO.MousePos; +} + +ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button]) + if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + return ImVec2(0.0f, 0.0f); +} + +void ImGui::ResetMouseDragDelta(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr + g.IO.MouseClickedPos[button] = g.IO.MousePos; +} + +ImGuiMouseCursor ImGui::GetMouseCursor() +{ + return GImGui->MouseCursor; +} + +void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) +{ + GImGui->MouseCursor = cursor_type; +} + +void ImGui::CaptureKeyboardFromApp(bool capture) +{ + GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0; +} + +void ImGui::CaptureMouseFromApp(bool capture) +{ + GImGui->CaptureMouseNextFrame = capture ? 1 : 0; +} + +bool ImGui::IsItemHovered() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemHoveredAndUsable; +} + +bool ImGui::IsItemHoveredRect() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemHoveredRect; +} + +bool ImGui::IsItemActive() +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId) + { + ImGuiWindow* window = GetCurrentWindowRead(); + return g.ActiveId == window->DC.LastItemId; + } + return false; +} + +bool ImGui::IsItemClicked(int mouse_button) +{ + return IsMouseClicked(mouse_button) && IsItemHovered(); +} + +bool ImGui::IsAnyItemHovered() +{ + return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0; +} + +bool ImGui::IsAnyItemActive() +{ + return GImGui->ActiveId != 0; +} + +bool ImGui::IsItemVisible() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImRect r(window->ClipRect); + return r.Overlaps(window->DC.LastItemRect); +} + +// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. +void ImGui::SetItemAllowOverlap() +{ + ImGuiContext& g = *GImGui; + if (g.HoveredId == g.CurrentWindow->DC.LastItemId) + g.HoveredIdAllowOverlap = true; + if (g.ActiveId == g.CurrentWindow->DC.LastItemId) + g.ActiveIdAllowOverlap = true; +} + +ImVec2 ImGui::GetItemRectMin() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.Min; +} + +ImVec2 ImGui::GetItemRectMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.Max; +} + +ImVec2 ImGui::GetItemRectSize() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.GetSize(); +} + +ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImRect rect = window->DC.LastItemRect; + rect.Expand(outward); + return rect.GetClosestPoint(pos, on_edge); +} + +// Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value. +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args); +} + +void ImGui::SetTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + SetTooltipV(fmt, args); + va_end(args); +} + +static ImRect GetVisibleRect() +{ + ImGuiContext& g = *GImGui; + if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) + return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); + return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); +} + +void ImGui::BeginTooltip() +{ + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + ImGui::Begin("##Tooltip", NULL, flags); +} + +void ImGui::EndTooltip() +{ + IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls + ImGui::End(); +} + +static bool IsPopupOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; +} + +// Mark popup as open (toggle toward open state). +// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) +void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetID(str_id); + int current_stack_size = g.CurrentPopupStack.Size; + ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) + if (g.OpenPopupStack.Size < current_stack_size + 1) + g.OpenPopupStack.push_back(popup_ref); + else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id) + { + g.OpenPopupStack.resize(current_stack_size+1); + g.OpenPopupStack[current_stack_size] = popup_ref; + } +} + +void ImGui::OpenPopup(const char* str_id) +{ + ImGui::OpenPopupEx(str_id, false); +} + +static void CloseInactivePopups() +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.empty()) + return; + + // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. + // Don't close our own child popup windows + int n = 0; + if (g.FocusedWindow) + { + for (n = 0; n < g.OpenPopupStack.Size; n++) + { + ImGuiPopupRef& popup = g.OpenPopupStack[n]; + if (!popup.Window) + continue; + IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); + if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + + bool has_focus = false; + for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) + has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow); + if (!has_focus) + break; + } + } + if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below + g.OpenPopupStack.resize(n); +} + +static ImGuiWindow* GetFrontMostModalRootWindow() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) + if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window) + if (front_most_popup->Flags & ImGuiWindowFlags_Modal) + return front_most_popup; + return NULL; +} + +static void ClosePopupToLevel(int remaining) +{ + ImGuiContext& g = *GImGui; + if (remaining > 0) + ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window); + else + ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow); + g.OpenPopupStack.resize(remaining); +} + +static void ClosePopup(ImGuiID id) +{ + if (!IsPopupOpen(id)) + return; + ImGuiContext& g = *GImGui; + ClosePopupToLevel(g.OpenPopupStack.Size - 1); +} + +// Close the popup we have begin-ed into. +void ImGui::CloseCurrentPopup() +{ + ImGuiContext& g = *GImGui; + int popup_idx = g.CurrentPopupStack.Size - 1; + if (popup_idx < 0 || popup_idx > g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) + return; + while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) + popup_idx--; + ClosePopupToLevel(popup_idx); +} + +static inline void ClearSetNextWindowData() +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0; + g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; +} + +static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(str_id); + if (!IsPopupOpen(id)) + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + + char name[20]; + if (flags & ImGuiWindowFlags_ChildMenu) + ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth + else + ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame + + bool is_open = ImGui::Begin(name, NULL, flags); + if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) + g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders; + if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + ImGui::EndPopup(); + + return is_open; +} + +bool ImGui::BeginPopup(const char* str_id) +{ + if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders); +} + +bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(name); + if (!IsPopupOpen(id)) + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + + ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings; + bool is_open = ImGui::Begin(name, p_open, flags); + if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + { + ImGui::EndPopup(); + if (is_open) + ClosePopup(id); + return false; + } + + return is_open; +} + +void ImGui::EndPopup() +{ + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls + IM_ASSERT(GImGui->CurrentPopupStack.Size > 0); + ImGui::End(); + if (!(window->Flags & ImGuiWindowFlags_Modal)) + ImGui::PopStyleVar(); +} + +// This is a helper to handle the most simple case of associating one named popup to one given widget. +// 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling +// this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers. +// 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect() +// and passing true to the OpenPopupEx(). +// Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that +// the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu +// driven by click position. +bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) +{ + if (IsItemHovered() && IsMouseClicked(mouse_button)) + OpenPopupEx(str_id, false); + return BeginPopup(str_id); +} + +bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button) +{ + if (!str_id) str_id = "window_context_menu"; + if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button)) + if (also_over_items || !IsAnyItemHovered()) + OpenPopupEx(str_id, true); + return BeginPopup(str_id); +} + +bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) +{ + if (!str_id) str_id = "void_context_menu"; + if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) + OpenPopupEx(str_id, true); + return BeginPopup(str_id); +} + +static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; + + const ImVec2 content_avail = ImGui::GetContentRegionAvail(); + ImVec2 size = ImFloor(size_arg); + if (size.x <= 0.0f) + { + if (size.x == 0.0f) + flags |= ImGuiWindowFlags_ChildWindowAutoFitX; + size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues) + } + if (size.y <= 0.0f) + { + if (size.y == 0.0f) + flags |= ImGuiWindowFlags_ChildWindowAutoFitY; + size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y); + } + if (border) + flags |= ImGuiWindowFlags_ShowBorders; + flags |= extra_flags; + + char title[256]; + if (name) + ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s.%08X", window->Name, name, id); + else + ImFormatString(title, IM_ARRAYSIZE(title), "%s.%08X", window->Name, id); + + bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags); + + if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) + ImGui::GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders; + + return ret; +} + +bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); +} + +bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) +{ + return BeginChildEx(NULL, id, size_arg, border, extra_flags); +} + +void ImGui::EndChild() +{ + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss + if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1) + { + ImGui::End(); + } + else + { + // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. + ImVec2 sz = GetWindowSize(); + if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f + sz.x = ImMax(4.0f, sz.x); + if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY) + sz.y = ImMax(4.0f, sz.y); + + ImGui::End(); + + window = GetCurrentWindow(); + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz); + ItemSize(sz); + ItemAdd(bb, NULL); + } +} + +// Helper to create a child window / scrolling region that looks like a normal widget frame. +bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]); + ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); + return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); +} + +void ImGui::EndChildFrame() +{ + ImGui::EndChild(); + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(); +} + +// Save and compare stack sizes on Begin()/End() to detect usage errors +static void CheckStacksSize(ImGuiWindow* window, bool write) +{ + // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) + ImGuiContext& g = *GImGui; + int* p_backup = &window->DC.StackSizesBackup[0]; + { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID Mismatch!"); p_backup++; } // User forgot PopID() + { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // User forgot EndGroup() + { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++; }// User forgot EndPopup()/EndMenu() + { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // User forgot PopStyleColor() + { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // User forgot PopStyleVar() + { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // User forgot PopFont() + IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); +} + +static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner) +{ + const ImGuiStyle& style = GImGui->Style; + + // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it. + ImVec2 safe_padding = style.DisplaySafeAreaPadding; + ImRect r_outer(GetVisibleRect()); + r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f)); + ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size); + + for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction). + { + const int dir = (n == -1) ? *last_dir : n; + ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y); + if (rect.GetWidth() < size.x || rect.GetHeight() < size.y) + continue; + *last_dir = dir; + return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y); + } + + // Fallback, try to keep within display + *last_dir = -1; + ImVec2 pos = base_pos; + pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); + pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); + return pos; +} + +ImGuiWindow* ImGui::FindWindowByName(const char* name) +{ + // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block + ImGuiContext& g = *GImGui; + ImGuiID id = ImHash(name, 0); + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i]->ID == id) + return g.Windows[i]; + return NULL; +} + +static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + + // Create window the first time + ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); + IM_PLACEMENT_NEW(window) ImGuiWindow(name); + window->Flags = flags; + + if (flags & ImGuiWindowFlags_NoSavedSettings) + { + // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. + window->Size = window->SizeFull = size; + } + else + { + // Retrieve settings from .ini file + // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. + window->PosFloat = ImVec2(60, 60); + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + + ImGuiIniData* settings = FindWindowSettings(name); + if (!settings) + { + settings = AddWindowSettings(name); + } + else + { + window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver; + } + + if (settings->Pos.x != FLT_MAX) + { + window->PosFloat = settings->Pos; + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->Collapsed = settings->Collapsed; + } + + if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize)) + size = settings->Size; + window->Size = window->SizeFull = size; + } + + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) + { + window->AutoFitFramesX = window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = false; + } + else + { + if (window->Size.x <= 0.0f) + window->AutoFitFramesX = 2; + if (window->Size.y <= 0.0f) + window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); + } + + if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) + g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once + else + g.Windows.push_back(window); + return window; +} + +static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size) +{ + ImGuiContext& g = *GImGui; + if (g.SetNextWindowSizeConstraint) + { + // Using -1,-1 on either X/Y axis to preserve the current size. + ImRect cr = g.SetNextWindowSizeConstraintRect; + new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; + new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; + if (g.SetNextWindowSizeConstraintCallback) + { + ImGuiSizeConstraintCallbackData data; + data.UserData = g.SetNextWindowSizeConstraintCallbackUserData; + data.Pos = window->Pos; + data.CurrentSize = window->SizeFull; + data.DesiredSize = new_size; + g.SetNextWindowSizeConstraintCallback(&data); + new_size = data.DesiredSize; + } + } + if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) + new_size = ImMax(new_size, g.Style.WindowMinSize); + window->SizeFull = new_size; +} + +// Push a new ImGui window to add widgets to. +// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. +// - Begin/End can be called multiple times during the frame with the same window name to append content. +// - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation. +// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). +// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. +// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. +// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. +// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin(). +bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) +{ + return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags); +} + +bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + IM_ASSERT(name != NULL); // Window name required + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet + + if (flags & ImGuiWindowFlags_NoInputs) + flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; + + // Find or create + bool window_is_new = false; + ImGuiWindow* window = FindWindowByName(name); + if (!window) + { + window = CreateNewWindow(name, size_on_first_use, flags); + window_is_new = true; + } + + const int current_frame = ImGui::GetFrameCount(); + const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); + if (first_begin_of_the_frame) + window->Flags = (ImGuiWindowFlags)flags; + else + flags = window->Flags; + + // Add to stack + ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL; + g.CurrentWindowStack.push_back(window); + SetCurrentWindow(window); + CheckStacksSize(window, true); + IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); + + bool window_was_active = (window->LastFrameActive == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on + if (flags & ImGuiWindowFlags_Popup) + { + ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + window_was_active &= (window->PopupId == popup_ref.PopupId); + window_was_active &= (window == popup_ref.Window); + popup_ref.Window = window; + g.CurrentPopupStack.push_back(popup_ref); + window->PopupId = popup_ref.PopupId; + } + + const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1); + + // Process SetNextWindow***() calls + bool window_pos_set_by_api = false, window_size_set_by_api = false; + if (g.SetNextWindowPosCond) + { + const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that. + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing; + window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0; + if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f) + { + window->SetWindowPosCenterWanted = true; // May be processed on the next frame if this is our first frame and we are measuring size + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + } + else + { + SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond); + } + window->DC.CursorPos = backup_cursor_pos; + g.SetNextWindowPosCond = 0; + } + if (g.SetNextWindowSizeCond) + { + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing; + window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0; + SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond); + g.SetNextWindowSizeCond = 0; + } + if (g.SetNextWindowContentSizeCond) + { + window->SizeContentsExplicit = g.SetNextWindowContentSizeVal; + g.SetNextWindowContentSizeCond = 0; + } + else if (first_begin_of_the_frame) + { + window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); + } + if (g.SetNextWindowCollapsedCond) + { + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing; + SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond); + g.SetNextWindowCollapsedCond = 0; + } + if (g.SetNextWindowFocus) + { + ImGui::SetWindowFocus(); + g.SetNextWindowFocus = false; + } + + // Update known root window (if we are a child window, otherwise window == window->RootWindow) + int root_idx, root_non_popup_idx; + for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--) + if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow)) + break; + for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--) + if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + break; + window->ParentWindow = parent_window; + window->RootWindow = g.CurrentWindowStack[root_idx]; + window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color. + + // When reusing window again multiple times a frame, just append content (don't need to setup again) + if (first_begin_of_the_frame) + { + window->Active = true; + window->IndexWithinParent = 0; + window->BeginCount = 0; + window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); + window->LastFrameActive = current_frame; + window->IDStack.resize(1); + + // Clear draw list, setup texture, outer clipping rectangle + window->DrawList->Clear(); + window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); + ImRect fullscreen_rect(GetVisibleRect()); + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup))) + PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); + else + PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true); + + if (!window_was_active) + { + // Popup first latch mouse position, will position itself when it appears next frame + window->AutoPosLastDirection = -1; + if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) + window->PosFloat = g.IO.MousePos; + } + + // Collapse window by double-clicking on title bar + // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) + { + ImRect title_bar_rect = window->TitleBarRect(); + if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + { + window->Collapsed = !window->Collapsed; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkIniSettingsDirty(); + FocusWindow(window); + } + } + else + { + window->Collapsed = false; + } + + // SIZE + + // Save contents size from last frame for auto-fitting (unless explicitly specified) + window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x)); + window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y)); + + // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) + if (window->HiddenFrames > 0) + window->HiddenFrames--; + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active) + { + window->HiddenFrames = 1; + if (flags & ImGuiWindowFlags_AlwaysAutoResize) + { + if (!window_size_set_by_api) + window->Size = window->SizeFull = ImVec2(0.f, 0.f); + window->SizeContents = ImVec2(0.f, 0.f); + } + } + + // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects. + window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding; + + // Calculate auto-fit size + ImVec2 size_auto_fit; + if ((flags & ImGuiWindowFlags_Tooltip) != 0) + { + // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. + size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); + } + else + { + size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); + + // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. + if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) + size_auto_fit.y += style.ScrollbarSize; + if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) + size_auto_fit.x += style.ScrollbarSize; + size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f); + } + + // Handle automatic resize + if (window->Collapsed) + { + // We still process initial auto-fit on collapsed windows to get a window width, + // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. + if (window->AutoFitFramesX > 0) + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + if (window->AutoFitFramesY > 0) + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + } + else + { + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api) + { + window->SizeFull = size_auto_fit; + } + else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api) + { + // Auto-fit only grows during the first few frames + if (window->AutoFitFramesX > 0) + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + if (window->AutoFitFramesY > 0) + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkIniSettingsDirty(); + } + } + + // Apply minimum/maximum window size constraints and final size + ApplySizeFullWithConstraint(window, window->SizeFull); + window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull; + + // POSITION + + // Position child window + if (flags & ImGuiWindowFlags_ChildWindow) + { + window->IndexWithinParent = parent_window->DC.ChildWindows.Size; + parent_window->DC.ChildWindows.push_back(window); + } + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) + { + window->Pos = window->PosFloat = parent_window->DC.CursorPos; + window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin(). + } + + bool window_pos_center = false; + window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0); + window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden); + if (window_pos_center) + { + // Center (any sort of window) + SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0); + } + else if (flags & ImGuiWindowFlags_ChildMenu) + { + IM_ASSERT(window_pos_set_by_api); + ImRect rect_to_avoid; + if (parent_window->DC.MenuBarAppending) + rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); + else + rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4) + window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden) + { + ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); + window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + + // Position tooltip (always follows mouse) + if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) + { + ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? + window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + if (window->AutoPosLastDirection == -1) + window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. + } + + // Clamp position so it stays visible + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + { + if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + { + ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size; + window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding); + } + } + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + + // Default item width. Make it proportional to window size if window manually resizes + if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) + window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); + else + window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f); + + // Prepare for focus requests + window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); + window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); + window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; + window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; + + // Apply scrolling + if (window->ScrollTarget.x < FLT_MAX) + { + window->Scroll.x = window->ScrollTarget.x; + window->ScrollTarget.x = FLT_MAX; + } + if (window->ScrollTarget.y < FLT_MAX) + { + float center_ratio = window->ScrollTargetCenterRatio.y; + window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y); + window->ScrollTarget.y = FLT_MAX; + } + window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f)); + if (!window->Collapsed && !window->SkipItems) + window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes)); + + // Modal window darkens what is behind them + if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) + window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + + // Draw window + handle manual resize + ImRect title_bar_rect = window->TitleBarRect(); + const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; + if (window->Collapsed) + { + // Draw title bar only + RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding); + } + else + { + ImU32 resize_col = 0; + const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize)) + { + // Manual resize + const ImVec2 br = window->Rect().GetBR(); + const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); + const ImGuiID resize_id = window->GetID("#RESIZE"); + bool hovered, held; + ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); + resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); + + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; + + if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) + { + // Manual auto-fit when double-clicking + ApplySizeFullWithConstraint(window, size_auto_fit); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkIniSettingsDirty(); + SetActiveID(0); + } + else if (held) + { + // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position + ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkIniSettingsDirty(); + } + + window->Size = window->SizeFull; + title_bar_rect = window->TitleBarRect(); + } + + // Scrollbars + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f; + + // Window background, Default Alpha + ImGuiCol bg_color_idx = ImGuiCol_WindowBg; + if ((flags & ImGuiWindowFlags_ComboBox) != 0) + bg_color_idx = ImGuiCol_ComboBg; + else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0) + bg_color_idx = ImGuiCol_PopupBg; + else if ((flags & ImGuiWindowFlags_ChildWindow) != 0) + bg_color_idx = ImGuiCol_ChildWindowBg; + ImVec4 bg_color = style.Colors[bg_color_idx]; + if (bg_alpha >= 0.0f) + bg_color.w = bg_alpha; + bg_color.w *= style.Alpha; + if (bg_color.w > 0.0f) + window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight); + + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); + + // Menu bar + if (flags & ImGuiWindowFlags_MenuBar) + { + ImRect menu_bar_rect = window->MenuBarRect(); + if (flags & ImGuiWindowFlags_ShowBorders) + window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border)); + window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); + } + + // Scrollbars + if (window->ScrollbarX) + Scrollbar(window, true); + if (window->ScrollbarY) + Scrollbar(window, false); + + // Render resize grip + // (after the input handling so we don't have a frame of latency) + if (!(flags & ImGuiWindowFlags_NoResize)) + { + const ImVec2 br = window->Rect().GetBR(); + window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize)); + window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size)); + window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3); + window->DrawList->PathFill(resize_col); + } + + // Borders + if (flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding); + window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding); + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border)); + } + } + + // Update ContentsRegionMax. All the variable it depends on are set above in this function. + window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x; + window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); + window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); + window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); + + // Setup drawing context + window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; + window->DC.GroupOffsetX = 0.0f; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); + window->DC.CursorPos = window->DC.CursorStartPos; + window->DC.CursorPosPrevLine = window->DC.CursorPos; + window->DC.CursorMaxPos = window->DC.CursorStartPos; + window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.MenuBarAppending = false; + window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + window->DC.ChildWindows.resize(0); + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ItemWidth = window->ItemWidthDefault; + window->DC.TextWrapPos = -1.0f; // disabled + window->DC.AllowKeyboardFocus = true; + window->DC.ButtonRepeat = false; + window->DC.ItemWidthStack.resize(0); + window->DC.AllowKeyboardFocusStack.resize(0); + window->DC.ButtonRepeatStack.resize(0); + window->DC.TextWrapPosStack.resize(0); + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsCount = 1; + window->DC.ColumnsStartPosY = window->DC.CursorPos.y; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY; + window->DC.TreeDepth = 0; + window->DC.StateStorage = &window->StateStorage; + window->DC.GroupStack.resize(0); + window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect; + window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active); + + if (window->AutoFitFramesX > 0) + window->AutoFitFramesX--; + if (window->AutoFitFramesY > 0) + window->AutoFitFramesY--; + + // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) + if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) + if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) + FocusWindow(window); + + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + { + if (p_open != NULL) + { + const float pad = 2.0f; + const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f; + if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad)) + *p_open = false; + } + + const ImVec2 text_size = CalcTextSize(name, NULL, true); + if (!(flags & ImGuiWindowFlags_NoCollapse)) + RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f); + + ImVec2 text_min = window->Pos; + ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y); + ImRect clip_rect; + clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton() + float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x; + float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x; + if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); + text_min.x += pad_left; + text_max.x -= pad_right; + clip_rect.Min = ImVec2(text_min.x, window->Pos.y); + RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); + } + + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->WindowRectClipped = window->Rect(); + window->WindowRectClipped.Clip(window->ClipRect); + + // Pressing CTRL+C while holding on a window copy its content to the clipboard + // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. + // Maybe we can support CTRL+C on every element? + /* + if (g.ActiveId == move_id) + if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + ImGui::LogToClipboard(); + */ + } + + // Inner clipping rectangle + // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame + // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. + const ImRect title_bar_rect = window->TitleBarRect(); + const float border_size = window->BorderSize; + ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size); + clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size); + PushClipRect(clip_rect.Min, clip_rect.Max, true); + + // Clear 'accessed' flag last thing + if (first_begin_of_the_frame) + window->Accessed = false; + window->BeginCount++; + g.SetNextWindowSizeConstraint = false; + + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). + if (flags & ImGuiWindowFlags_ChildWindow) + { + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + window->Collapsed = parent_window && parent_window->Collapsed; + + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y); + + // We also hide the window from rendering because we've already added its border to the command list. + // (we could perform the check earlier in the function but it is simpler at this point) + if (window->Collapsed) + window->Active = false; + } + if (style.Alpha <= 0.0f) + window->Active = false; + + // Return false if we don't intend to display anything to allow user to perform an early out optimization + window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0; + return !window->SkipItems; +} + +void ImGui::End() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + Columns(1, "#CloseColumns"); + PopClipRect(); // inner window clip rectangle + + // Stop logging + if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging + LogFinish(); + + // Pop + // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin(). + g.CurrentWindowStack.pop_back(); + if (window->Flags & ImGuiWindowFlags_Popup) + g.CurrentPopupStack.pop_back(); + CheckStacksSize(window, false); + SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); +} + +// Vertical scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +static void Scrollbar(ImGuiWindow* window, bool horizontal) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); + + // Render background + bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); + float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; + const ImRect window_rect = window->Rect(); + const float border_size = window->BorderSize; + ImRect bb = horizontal + ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) + : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); + if (!horizontal) + bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); + + float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; + int window_rounding_corners; + if (horizontal) + window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight); + else + window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight); + window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); + bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); + + // V denote the main axis of the scrollbar + float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); + float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; + float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w; + float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; + + // The grabable box size generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + const bool previously_held = (g.ActiveId == id); + ImGui::ButtonBehavior(bb, id, &hovered, &held); + + float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); + float scroll_ratio = ImSaturate(scroll_v / scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + if (held && grab_h_norm < 1.0f) + { + float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; + float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); + ImGui::SetHoveredID(id); + + bool seek_absolute = false; + if (!previously_held) + { + // On initial click calculate the distance between mouse and the center of the grab + if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) + { + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + else + { + seek_absolute = true; + *click_delta_to_grab_center_v = 0.0f; + } + } + + // Apply scroll + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position + const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + if (horizontal) + window->Scroll.x = scroll_v; + else + window->Scroll.y = scroll_v; + + // Update values for rendering + scroll_ratio = ImSaturate(scroll_v / scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + + // Render + const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + if (horizontal) + window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding); + else + window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding); +} + +// Moving window to front of display (which happens to be back of our sorted list) +void ImGui::FocusWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + + // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing. + g.FocusedWindow = window; + + // Passing NULL allow to disable keyboard focus + if (!window) + return; + + // And move its root window to the top of the pile + if (window->RootWindow) + window = window->RootWindow; + + // Steal focus on active widgets + if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it.. + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window) + SetActiveID(0); + + // Bring to front + if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window) + return; + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i] == window) + { + g.Windows.erase(g.Windows.begin() + i); + break; + } + g.Windows.push_back(window); +} + +void ImGui::PushItemWidth(float item_width) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); +} + +static void PushMultiItemsWidths(int components, float w_full) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImGuiStyle& style = GImGui->Style; + if (w_full <= 0.0f) + w_full = ImGui::CalcItemWidth(); + const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + window->DC.ItemWidthStack.push_back(w_item_last); + for (int i = 0; i < components-1; i++) + window->DC.ItemWidthStack.push_back(w_item_one); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); +} + +void ImGui::PopItemWidth() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidthStack.pop_back(); + window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); +} + +float ImGui::CalcItemWidth() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + float w = window->DC.ItemWidth; + if (w < 0.0f) + { + // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. + float width_to_right_edge = GetContentRegionAvail().x; + w = ImMax(1.0f, width_to_right_edge + w); + } + w = (float)(int)w; + return w; +} + +static ImFont* GetDefaultFont() +{ + ImGuiContext& g = *GImGui; + return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; +} + +static void SetCurrentFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(font->Scale > 0.0f); + g.Font = font; + g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; + g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel; +} + +void ImGui::PushFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + if (!font) + font = GetDefaultFont(); + SetCurrentFont(font); + g.FontStack.push_back(font); + g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); +} + +void ImGui::PopFont() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DrawList->PopTextureID(); + g.FontStack.pop_back(); + SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); +} + +void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.AllowKeyboardFocus = allow_keyboard_focus; + window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus); +} + +void ImGui::PopAllowKeyboardFocus() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.AllowKeyboardFocusStack.pop_back(); + window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back(); +} + +void ImGui::PushButtonRepeat(bool repeat) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ButtonRepeat = repeat; + window->DC.ButtonRepeatStack.push_back(repeat); +} + +void ImGui::PopButtonRepeat() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ButtonRepeatStack.pop_back(); + window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back(); +} + +void ImGui::PushTextWrapPos(float wrap_pos_x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPos = wrap_pos_x; + window->DC.TextWrapPosStack.push_back(wrap_pos_x); +} + +void ImGui::PopTextWrapPos() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPosStack.pop_back(); + window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); +} + +void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) +{ + ImGuiContext& g = *GImGui; + ImGuiColMod backup; + backup.Col = idx; + backup.BackupValue = g.Style.Colors[idx]; + g.ColorModifiers.push_back(backup); + g.Style.Colors[idx] = col; +} + +void ImGui::PopStyleColor(int count) +{ + ImGuiContext& g = *GImGui; + while (count > 0) + { + ImGuiColMod& backup = g.ColorModifiers.back(); + g.Style.Colors[backup.Col] = backup.BackupValue; + g.ColorModifiers.pop_back(); + count--; + } +} + +struct ImGuiStyleVarInfo +{ + ImGuiDataType Type; + ImU32 Offset; + void* GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); } +}; + +static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] = +{ + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, +}; + +static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) +{ + IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_); + return &GStyleVarInfo[idx]; +} + +void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) +{ + const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type == ImGuiDataType_Float) + { + float* pvar = (float*)var_info->GetVarPtr(); + GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; + return; + } + IM_ASSERT(0); // Called function with wrong-type? Variable is not a float. +} + +void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) +{ + const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type == ImGuiDataType_Float2) + { + ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(); + GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; + return; + } + IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2. +} + +void ImGui::PopStyleVar(int count) +{ + ImGuiContext& g = *GImGui; + while (count > 0) + { + ImGuiStyleMod& backup = g.StyleModifiers.back(); + const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); + if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr()) = backup.BackupFloat[0]; + else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]); + else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr()) = backup.BackupInt[0]; + g.StyleModifiers.pop_back(); + count--; + } +} + +const char* ImGui::GetStyleColName(ImGuiCol idx) +{ + // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; + switch (idx) + { + case ImGuiCol_Text: return "Text"; + case ImGuiCol_TextDisabled: return "TextDisabled"; + case ImGuiCol_WindowBg: return "WindowBg"; + case ImGuiCol_ChildWindowBg: return "ChildWindowBg"; + case ImGuiCol_PopupBg: return "PopupBg"; + case ImGuiCol_Border: return "Border"; + case ImGuiCol_BorderShadow: return "BorderShadow"; + case ImGuiCol_FrameBg: return "FrameBg"; + case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; + case ImGuiCol_FrameBgActive: return "FrameBgActive"; + case ImGuiCol_TitleBg: return "TitleBg"; + case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; + case ImGuiCol_TitleBgActive: return "TitleBgActive"; + case ImGuiCol_MenuBarBg: return "MenuBarBg"; + case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; + case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; + case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; + case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; + case ImGuiCol_ComboBg: return "ComboBg"; + case ImGuiCol_CheckMark: return "CheckMark"; + case ImGuiCol_SliderGrab: return "SliderGrab"; + case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; + case ImGuiCol_Button: return "Button"; + case ImGuiCol_ButtonHovered: return "ButtonHovered"; + case ImGuiCol_ButtonActive: return "ButtonActive"; + case ImGuiCol_Header: return "Header"; + case ImGuiCol_HeaderHovered: return "HeaderHovered"; + case ImGuiCol_HeaderActive: return "HeaderActive"; + case ImGuiCol_Column: return "Column"; + case ImGuiCol_ColumnHovered: return "ColumnHovered"; + case ImGuiCol_ColumnActive: return "ColumnActive"; + case ImGuiCol_ResizeGrip: return "ResizeGrip"; + case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; + case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; + case ImGuiCol_CloseButton: return "CloseButton"; + case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered"; + case ImGuiCol_CloseButtonActive: return "CloseButtonActive"; + case ImGuiCol_PlotLines: return "PlotLines"; + case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; + case ImGuiCol_PlotHistogram: return "PlotHistogram"; + case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; + case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; + case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; + } + IM_ASSERT(0); + return "Unknown"; +} + +bool ImGui::IsWindowHovered() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow); +} + +bool ImGui::IsWindowFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow == g.CurrentWindow; +} + +bool ImGui::IsRootWindowFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow == g.CurrentWindow->RootWindow; +} + +bool ImGui::IsRootWindowOrAnyChildFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow; +} + +bool ImGui::IsRootWindowOrAnyChildHovered() +{ + ImGuiContext& g = *GImGui; + return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow); +} + +float ImGui::GetWindowWidth() +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Size.x; +} + +float ImGui::GetWindowHeight() +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Size.y; +} + +ImVec2 ImGui::GetWindowPos() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + return window->Pos; +} + +static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) +{ + window->DC.CursorMaxPos.y += window->Scroll.y; + window->Scroll.y = new_scroll_y; + window->DC.CursorMaxPos.y -= window->Scroll.y; +} + +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowPosAllowFlags & cond) == 0) + return; + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + window->SetWindowPosCenterWanted = false; + + // Set + const ImVec2 old_pos = window->Pos; + window->PosFloat = pos; + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor + window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. +} + +void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + SetWindowPos(window, pos, cond); +} + +void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond) +{ + if (ImGuiWindow* window = FindWindowByName(name)) + SetWindowPos(window, pos, cond); +} + +ImVec2 ImGui::GetWindowSize() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->Size; +} + +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) + return; + window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + if (size.x > 0.0f) + { + window->AutoFitFramesX = 0; + window->SizeFull.x = size.x; + } + else + { + window->AutoFitFramesX = 2; + window->AutoFitOnlyGrows = false; + } + if (size.y > 0.0f) + { + window->AutoFitFramesY = 0; + window->SizeFull.y = size.y; + } + else + { + window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = false; + } +} + +void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + SetWindowSize(GImGui->CurrentWindow, size, cond); +} + +void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowSize(window, size, cond); +} + +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) + return; + window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + window->Collapsed = collapsed; +} + +void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); +} + +bool ImGui::IsWindowCollapsed() +{ + return GImGui->CurrentWindow->Collapsed; +} + +void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowCollapsed(window, collapsed, cond); +} + +void ImGui::SetWindowFocus() +{ + FocusWindow(GImGui->CurrentWindow); +} + +void ImGui::SetWindowFocus(const char* name) +{ + if (name) + { + if (ImGuiWindow* window = FindWindowByName(name)) + FocusWindow(window); + } + else + { + FocusWindow(NULL); + } +} + +void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosVal = pos; + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX); + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowSizeVal = size; + g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowSizeConstraint = true; + g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max); + g.SetNextWindowSizeConstraintCallback = custom_callback; + g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data; +} + +void ImGui::SetNextWindowContentSize(const ImVec2& size) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowContentSizeVal = size; + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowContentWidth(float width) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f); + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowCollapsedVal = collapsed; + g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowFocus() +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowFocus = true; +} + +// In window space (not screen space!) +ImVec2 ImGui::GetContentRegionMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImVec2 mx = window->ContentsRegionRect.Max; + if (window->DC.ColumnsCount != 1) + mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x; + return mx; +} + +ImVec2 ImGui::GetContentRegionAvail() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return GetContentRegionMax() - (window->DC.CursorPos - window->Pos); +} + +float ImGui::GetContentRegionAvailWidth() +{ + return GetContentRegionAvail().x; +} + +// In window space (not screen space!) +ImVec2 ImGui::GetWindowContentRegionMin() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Min; +} + +ImVec2 ImGui::GetWindowContentRegionMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Max; +} + +float ImGui::GetWindowContentRegionWidth() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x; +} + +float ImGui::GetTextLineHeight() +{ + ImGuiContext& g = *GImGui; + return g.FontSize; +} + +float ImGui::GetTextLineHeightWithSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + g.Style.ItemSpacing.y; +} + +float ImGui::GetItemsLineHeightWithSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; +} + +ImDrawList* ImGui::GetWindowDrawList() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DrawList; +} + +ImFont* ImGui::GetFont() +{ + return GImGui->Font; +} + +float ImGui::GetFontSize() +{ + return GImGui->FontSize; +} + +ImVec2 ImGui::GetFontTexUvWhitePixel() +{ + return GImGui->FontTexUvWhitePixel; +} + +void ImGui::SetWindowFontScale(float scale) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->FontWindowScale = scale; + g.FontSize = window->CalcFontSize(); +} + +// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. +// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'. +ImVec2 ImGui::GetCursorPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos - window->Pos + window->Scroll; +} + +float ImGui::GetCursorPosX() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; +} + +float ImGui::GetCursorPosY() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; +} + +void ImGui::SetCursorPos(const ImVec2& local_pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos = window->Pos - window->Scroll + local_pos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +} + +void ImGui::SetCursorPosX(float x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); +} + +void ImGui::SetCursorPosY(float y) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); +} + +ImVec2 ImGui::GetCursorStartPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorStartPos - window->Pos; +} + +ImVec2 ImGui::GetCursorScreenPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos; +} + +void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos = screen_pos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +} + +float ImGui::GetScrollX() +{ + return GImGui->CurrentWindow->Scroll.x; +} + +float ImGui::GetScrollY() +{ + return GImGui->CurrentWindow->Scroll.y; +} + +float ImGui::GetScrollMaxX() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x; +} + +float ImGui::GetScrollMaxY() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y; +} + +void ImGui::SetScrollX(float scroll_x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->ScrollTarget.x = scroll_x; + window->ScrollTargetCenterRatio.x = 0.0f; +} + +void ImGui::SetScrollY(float scroll_y) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY + window->ScrollTargetCenterRatio.y = 0.0f; +} + +void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) +{ + // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); + window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); + if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y) + window->ScrollTarget.y = 0.0f; + window->ScrollTargetCenterRatio.y = center_y_ratio; +} + +// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. +void ImGui::SetScrollHere(float center_y_ratio) +{ + ImGuiWindow* window = GetCurrentWindow(); + float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. + SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio); +} + +void ImGui::SetKeyboardFocusHere(int offset) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; + window->FocusIdxTabRequestNext = INT_MAX; +} + +void ImGui::SetStateStorage(ImGuiStorage* tree) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.StateStorage = tree ? tree : &window->StateStorage; +} + +ImGuiStorage* ImGui::GetStateStorage() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.StateStorage; +} + +void ImGui::TextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + TextUnformatted(g.TempBuffer, text_end); +} + +void ImGui::Text(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextV(fmt, args); + va_end(args); +} + +void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, col); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextColoredV(col, fmt, args); + va_end(args); +} + +void ImGui::TextDisabledV(const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextDisabled(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextDisabledV(fmt, args); + va_end(args); +} + +void ImGui::TextWrappedV(const char* fmt, va_list args) +{ + bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set + if (need_wrap) PushTextWrapPos(0.0f); + TextV(fmt, args); + if (need_wrap) PopTextWrapPos(); +} + +void ImGui::TextWrapped(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextWrappedV(fmt, args); + va_end(args); +} + +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(text != NULL); + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = wrap_pos_x >= 0.0f; + if (text_end - text > 2000 && !wrap_enabled) + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + const char* line = text; + const float line_height = GetTextLineHeight(); + const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset); + const ImRect clip_rect = window->ClipRect; + ImVec2 text_size(0,0); + + if (text_pos.y <= clip_rect.Max.y) + { + ImVec2 pos = text_pos; + + // Lines to skip (can't skip when logging text) + if (!g.LogEnabled) + { + int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (IsClippedEx(line_rect, NULL, false)) + break; + + const ImVec2 line_size = CalcTextSize(line, line_end, false); + text_size.x = ImMax(text_size.x, line_size.x); + RenderText(pos, line, line_end, false); + if (!line_end) + line_end = text_end; + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + + text_size.y += (pos - text_pos).y; + } + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(bb); + ItemAdd(bb, NULL); + } + else + { + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + // Account of baseline offset + ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size); + if (!ItemAdd(bb, NULL)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); + } +} + +void ImGui::AlignFirstTextHeightToWidgets() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height. + ImGuiContext& g = *GImGui; + ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y); + SameLine(0, 0); +} + +// Add a label+text combo aligned to other label+value widgets +void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); + const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) + return; + + // Render + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); +} + +void ImGui::LabelText(const char* label, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + LabelTextV(label, fmt, args); + va_end(args); +} + +static inline bool IsWindowContentHoverable(ImGuiWindow* window) +{ + // An active popup disable hovering on other windows (apart from its own children) + ImGuiContext& g = *GImGui; + if (ImGuiWindow* focused_window = g.FocusedWindow) + if (ImGuiWindow* focused_root_window = focused_window->RootWindow) + if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow) + return false; + + return true; +} + +bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (flags & ImGuiButtonFlags_Disabled) + { + if (out_hovered) *out_hovered = false; + if (out_held) *out_held = false; + if (g.ActiveId == id) SetActiveID(0); + return false; + } + + if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) + flags |= ImGuiButtonFlags_PressedOnClickRelease; + + bool pressed = false; + bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0); + if (hovered) + { + SetHoveredID(id); + if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) + { + // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat + // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds + // PressedOnClick | | .. + // PressedOnRelease | | .. (NOT on release) + // PressedOnDoubleClick | | .. + if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); // Hold on ID + FocusWindow(window); + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + } + if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) + { + pressed = true; + SetActiveID(0); + FocusWindow(window); + } + if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) + { + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + SetActiveID(0); + } + + // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). + // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. + if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) + pressed = true; + } + } + + bool held = false; + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + held = true; + } + else + { + if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + SetActiveID(0); + } + } + + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. + if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) + hovered = pressed = held = false; + + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + + return pressed; +} + +bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) + pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, &id)) + return false; + + if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + return pressed; +} + +bool ImGui::Button(const char* label, const ImVec2& size_arg) +{ + return ButtonEx(label, size_arg, 0); +} + +// Small buttons fits within text without additional vertical spacing. +bool ImGui::SmallButton(const char* label) +{ + ImGuiContext& g = *GImGui; + float backup_padding_y = g.Style.FramePadding.y; + g.Style.FramePadding.y = 0.0f; + bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine); + g.Style.FramePadding.y = backup_padding_y; + return pressed; +} + +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiID id = window->GetID(str_id); + ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + return pressed; +} + +// Upper-right button to close a window. +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) +{ + ImGuiWindow* window = GetCurrentWindow(); + + const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); + const ImVec2 center = bb.GetCenter(); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); + + const float cross_extent = (radius * 0.7071f) - 1.0f; + if (hovered) + { + window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); + window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); + } + + return pressed; +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col.w > 0.0f) + bb.Max += ImVec2(2,2); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + if (border_col.w > 0.0f) + { + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col)); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); + } +} + +// frame_padding < 0: uses FramePadding from style (default) +// frame_padding = 0: no framing +// frame_padding > 0: set framing size +// The color used are the button colors. +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. + PushID((void *)user_texture_id); + const ImGuiID id = window->GetID("#image"); + PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} + +// Start logging ImGui output to TTY +void ImGui::LogToTTY(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + g.LogEnabled = true; + g.LogFile = stdout; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to given file +void ImGui::LogToFile(int max_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + if (!filename) + { + filename = g.IO.LogFilename; + if (!filename) + return; + } + + g.LogFile = ImFileOpen(filename, "ab"); + if (!g.LogFile) + { + IM_ASSERT(g.LogFile != NULL); // Consider this an error + return; + } + g.LogEnabled = true; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to clipboard +void ImGui::LogToClipboard(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + g.LogEnabled = true; + g.LogFile = NULL; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +void ImGui::LogFinish() +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogText(IM_NEWLINE); + g.LogEnabled = false; + if (g.LogFile != NULL) + { + if (g.LogFile == stdout) + fflush(g.LogFile); + else + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard->size() > 1) + { + SetClipboardText(g.LogClipboard->begin()); + g.LogClipboard->clear(); + } +} + +// Helper to display logging buttons +void ImGui::LogButtons() +{ + ImGuiContext& g = *GImGui; + + PushID("LogButtons"); + const bool log_to_tty = Button("Log To TTY"); SameLine(); + const bool log_to_file = Button("Log To File"); SameLine(); + const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); + PushItemWidth(80.0f); + PushAllowKeyboardFocus(false); + SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); + PopAllowKeyboardFocus(); + PopItemWidth(); + PopID(); + + // Start logging at the end of the function so that the buttons don't appear in the log + if (log_to_tty) + LogToTTY(g.LogAutoExpandMaxDepth); + if (log_to_file) + LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); + if (log_to_clipboard) + LogToClipboard(g.LogAutoExpandMaxDepth); +} + +bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +{ + if (flags & ImGuiTreeNodeFlags_Leaf) + return true; + + // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions) + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStorage* storage = window->DC.StateStorage; + + bool is_open; + if (g.SetNextTreeNodeOpenCond != 0) + { + if (g.SetNextTreeNodeOpenCond & ImGuiSetCond_Always) + { + is_open = g.SetNextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + is_open = g.SetNextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + is_open = stored_value != 0; + } + } + g.SetNextTreeNodeOpenCond = 0; + } + else + { + is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + } + + // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). + // NB- If we are above max depth we still allow manually opened nodes to be logged. + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + is_open = true; + + return is_open; +} + +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; + const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); + + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + + // We vertically grow up to current line height up the typical widget height. + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it + const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); + ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); + if (display_frame) + { + // Framed header expand a little outside the default padding + bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; + bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; + } + + const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing + const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser + ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); + + // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing + // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not) + const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); + bool is_open = TreeNodeBehaviorIsOpen(id, flags); + if (!ItemAdd(interact_bb, &id)) + { + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; + } + + // Flags that affects opening behavior: + // - 0(default) ..................... single-click anywhere to open + // - OpenOnDoubleClick .............. double-click anywhere to open + // - OpenOnArrow .................... single-click on arrow to open + // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open + ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf)) + { + bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)); + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + toggled |= g.IO.MouseDoubleClicked[0]; + if (toggled) + { + is_open = !is_open; + window->DC.StateStorage->SetInt(id, is_open); + } + } + if (flags & ImGuiTreeNodeFlags_AllowOverlapMode) + SetItemAllowOverlap(); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y); + if (display_frame) + { + // Framed type + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f); + if (g.LogEnabled) + { + // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. + const char log_prefix[] = "\n##"; + const char log_suffix[] = "##"; + LogRenderedText(text_pos, log_prefix, log_prefix+3); + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + LogRenderedText(text_pos, log_suffix+1, log_suffix+3); + } + else + { + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + } + } + else + { + // Unframed typed for tree nodes + if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) + RenderFrame(bb.Min, bb.Max, col, false); + + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); + else if (!(flags & ImGuiTreeNodeFlags_Leaf)) + RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f); + if (g.LogEnabled) + LogRenderedText(text_pos, ">"); + RenderText(text_pos, label, label_end, false); + } + + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; +} + +// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). +// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label); +} + +bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (p_open && !*p_open) + return false; + + ImGuiID id = window->GetID(label); + bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label); + if (p_open) + { + // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + ImGuiContext& g = *GImGui; + float button_sz = g.FontSize * 0.5f; + if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) + *p_open = false; + } + + return is_open; +} + +bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags, label, NULL); +} + +bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +{ + return TreeNodeExV(str_id, 0, fmt, args); +} + +bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) +{ + return TreeNodeExV(ptr_id, 0, fmt, args); +} + +bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + return TreeNodeBehavior(window->GetID(label), 0, label, NULL); +} + +void ImGui::TreeAdvanceToLabelPos() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); +} + +// Horizontal distance preceding label when using TreeNode() or Bullet() +float ImGui::GetTreeNodeToLabelSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + (g.Style.FramePadding.x * 2.0f); +} + +void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextTreeNodeOpenVal = is_open; + g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::PushID(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(str_id)); +} + +void ImGui::PushID(const char* str_id_begin, const char* str_id_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); +} + +void ImGui::PushID(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(ptr_id)); +} + +void ImGui::PushID(int int_id) +{ + const void* ptr_id = (void*)(intptr_t)int_id; + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(ptr_id)); +} + +void ImGui::PopID() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.pop_back(); +} + +ImGuiID ImGui::GetID(const char* str_id) +{ + return GImGui->CurrentWindow->GetID(str_id); +} + +ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) +{ + return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); +} + +ImGuiID ImGui::GetID(const void* ptr_id) +{ + return GImGui->CurrentWindow->GetID(ptr_id); +} + +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + { + SameLine(0, style.FramePadding.x*2); + return; + } + + // Render and stay on same line + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + SameLine(0, style.FramePadding.x*2); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + // Render + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size) +{ + if (data_type == ImGuiDataType_Int) + ImFormatString(buf, buf_size, display_format, *(int*)data_ptr); + else if (data_type == ImGuiDataType_Float) + ImFormatString(buf, buf_size, display_format, *(float*)data_ptr); +} + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size) +{ + if (data_type == ImGuiDataType_Int) + { + if (decimal_precision < 0) + ImFormatString(buf, buf_size, "%d", *(int*)data_ptr); + else + ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr); + } + else if (data_type == ImGuiDataType_Float) + { + if (decimal_precision < 0) + ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits? + else + ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr); + } +} + +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1 +{ + if (data_type == ImGuiDataType_Int) + { + if (op == '+') + *(int*)value1 = *(int*)value1 + *(const int*)value2; + else if (op == '-') + *(int*)value1 = *(int*)value1 - *(const int*)value2; + } + else if (data_type == ImGuiDataType_Float) + { + if (op == '+') + *(float*)value1 = *(float*)value1 + *(const float*)value2; + else if (op == '-') + *(float*)value1 = *(float*)value1 - *(const float*)value2; + } +} + +// User can input math operators (e.g. +100) to edit a numerical values. +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format) +{ + while (ImCharIsSpace(*buf)) + buf++; + + // We don't support '-' op because it would conflict with inputing negative value. + // Instead you can use +-100 to subtract from an existing value + char op = buf[0]; + if (op == '+' || op == '*' || op == '/') + { + buf++; + while (ImCharIsSpace(*buf)) + buf++; + } + else + { + op = 0; + } + if (!buf[0]) + return false; + + if (data_type == ImGuiDataType_Int) + { + if (!scalar_format) + scalar_format = "%d"; + int* v = (int*)data_ptr; + const int old_v = *v; + int arg0 = *v; + if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1) + return false; + + // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision + float arg1 = 0.0f; + if (op == '+') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); } // Add (use "+-" to subtract) + else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); } // Multiply + else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide + else { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; } // Assign constant + return (old_v != *v); + } + else if (data_type == ImGuiDataType_Float) + { + // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in + scalar_format = "%f"; + float* v = (float*)data_ptr; + const float old_v = *v; + float arg0 = *v; + if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1) + return false; + + float arg1 = 0.0f; + if (sscanf(buf, scalar_format, &arg1) < 1) + return false; + if (op == '+') { *v = arg0 + arg1; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0 * arg1; } // Multiply + else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; } // Divide + else { *v = arg1; } // Assign constant + return (old_v != *v); + } + + return false; +} + +// Create text input in place of a slider (when CTRL+Clicking on slider) +bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) + SetActiveID(g.ScalarAsInputTextId, window); + SetHoveredID(0); + FocusableItemUnregister(window); + + char buf[32]; + DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf)); + bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); + if (g.ScalarAsInputTextId == 0) + { + // First frame + IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) + g.ScalarAsInputTextId = g.ActiveId; + SetHoveredID(id); + } + else if (g.ActiveId != g.ScalarAsInputTextId) + { + // Release + g.ScalarAsInputTextId = 0; + } + if (text_value_changed) + return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL); + return false; +} + +// Parse display precision back from the display format string +int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) +{ + int precision = default_precision; + while ((fmt = strchr(fmt, '%')) != NULL) + { + fmt++; + if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%" + while (*fmt >= '0' && *fmt <= '9') + fmt++; + if (*fmt == '.') + { + precision = atoi(fmt + 1); + if (precision < 0 || precision > 10) + precision = default_precision; + } + break; + } + return precision; +} + +float ImGui::RoundScalar(float value, int decimal_precision) +{ + // Round past decimal precision + // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 + // FIXME: Investigate better rounding methods + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); + bool negative = value < 0.0f; + value = fabsf(value); + float remainder = fmodf(value, min_step); + if (remainder <= min_step*0.5f) + value -= remainder; + else + value += (min_step - remainder); + return negative ? -value : value; +} + +static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) +{ + if (v_min == v_max) + return 0.0f; + + const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); + const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); + if (is_non_linear) + { + if (v_clamped < 0.0f) + { + const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min); + return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos; + } + else + { + const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); + return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); + } + } + + // Linear slider + return (v_clamped - v_min) / (v_max - v_min); +} + +bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = g.Style; + + // Draw frame + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); + const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; + + const float grab_padding = 2.0f; + const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); + float grab_sz; + if (decimal_precision > 0) + grab_sz = ImMin(style.GrabMinSize, slider_sz); + else + grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f; + const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f; + + // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f + float linear_zero_pos = 0.0f; // 0.0->1.0f + if (v_min * v_max < 0.0f) + { + // Different sign + const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); + const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); + linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); + } + else + { + // Same sign + linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; + } + + // Process clicking on the slider + bool value_changed = false; + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; + if (!is_horizontal) + clicked_t = 1.0f - clicked_t; + + float new_value; + if (is_non_linear) + { + // Account for logarithmic scale on both sides of the zero + if (clicked_t < linear_zero_pos) + { + // Negative: rescale to the negative range before powering + float a = 1.0f - (clicked_t / linear_zero_pos); + a = powf(a, power); + new_value = ImLerp(ImMin(v_max,0.0f), v_min, a); + } + else + { + // Positive: rescale to the positive range before powering + float a; + if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f) + a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); + else + a = clicked_t; + a = powf(a, power); + new_value = ImLerp(ImMax(v_min,0.0f), v_max, a); + } + } + else + { + // Linear slider + new_value = ImLerp(v_min, v_max, clicked_t); + } + + // Round past decimal precision + new_value = RoundScalar(new_value, decimal_precision); + if (*v != new_value) + { + *v = new_value; + value_changed = true; + } + } + else + { + SetActiveID(0); + } + } + + // Calculate slider grab positioning + float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); + + // Draw + if (!is_horizontal) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + ImRect grab_bb; + if (is_horizontal) + grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding)); + else + grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f)); + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + return value_changed; +} + +// Use power!=1.0 for logarithmic sliders. +// Adjust display_format to decorate the value with a prefix or a suffix. +// "%.3f" 1.234 +// "%5.2f secs" 01.23 secs +// "Gold: %.0f" Gold: 1 +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + // Tabbing or CTRL-clicking on Slider turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + { + SetActiveID(id, window); + FocusWindow(window); + + if (tab_focus_requested || g.IO.KeyCtrl) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); + + ItemSize(total_bb, style.FramePadding.y); + + // Actual slider behavior + render grab + const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, &id)) + return false; + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + if (hovered && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); + FocusWindow(window); + } + + // Actual slider behavior + render grab + bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) +{ + float v_deg = (*v_rad) * 360.0f / (2*IM_PI); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); + *v_rad = v_deg * (2*IM_PI) / 360.0f; + return value_changed; +} + +bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); + *v = (int)v_f; + return value_changed; +} + +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); + *v = (int)v_f; + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 2, v_min, v_max, display_format, power); +} + +bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 3, v_min, v_max, display_format, power); +} + +bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 4, v_min, v_max, display_format, power); +} + +bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 2, v_min, v_max, display_format); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 3, v_min, v_max, display_format); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 4, v_min, v_max, display_format); +} + +bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + bool value_changed = false; + + // Process clicking on the drag + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + if (g.ActiveIdIsJustActivated) + { + // Lock current value on click + g.DragCurrentValue = *v; + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + } + + float v_cur = g.DragCurrentValue; + const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); + if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) + { + float speed = v_speed; + if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) + speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + speed = speed * g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + speed = speed * g.DragSpeedScaleSlow; + + float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed; + if (fabsf(power - 1.0f) > 0.001f) + { + // Logarithmic curve on both side of 0.0 + float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; + float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; + float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign); + float v1_abs = v1 >= 0.0f ? v1 : -v1; + float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line + v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign + } + else + { + v_cur += delta; + } + g.DragLastMouseDelta.x = mouse_drag_delta.x; + + // Clamp + if (v_min < v_max) + v_cur = ImClamp(v_cur, v_min, v_max); + g.DragCurrentValue = v_cur; + } + + // Round to user desired precision, then apply + v_cur = RoundScalar(v_cur, decimal_precision); + if (*v != v_cur) + { + *v = v_cur; + value_changed = true; + } + } + else + { + SetActiveID(0); + } + } + + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0]))) + { + SetActiveID(id, window); + FocusWindow(window); + + if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); + + // Actual drag behavior + ItemSize(total_bb, style.FramePadding.y); + const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + + return value_changed; +} + +bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + + bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format); + *v = (int)v_f; + return value_changed; +} + +bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + + bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + if (graph_size.x == 0.0f) + graph_size.x = CalcItemWidth(); + if (graph_size.y == 0.0f) + graph_size.y = label_size.y + (style.FramePadding.y * 2); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) + return; + + // Determine scale from values if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v_min = FLT_MAX; + float v_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + const float v = values_getter(data, i); + v_min = ImMin(v_min, v); + v_max = ImMax(v_max, v); + } + if (scale_min == FLT_MAX) + scale_min = v_min; + if (scale_max == FLT_MAX) + scale_max = v_max; + } + + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + if (values_count > 0) + { + int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + + // Tooltip on hover + int v_hovered = -1; + if (IsHovered(inner_bb, 0)) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * item_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = values_getter(data, (v_idx + values_offset) % values_count); + const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); + if (plot_type == ImGuiPlotType_Lines) + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); + else if (plot_type == ImGuiPlotType_Histogram) + SetTooltip("%d: %8.4g", v_idx, v0); + v_hovered = v_idx; + } + + const float t_step = 1.0f / (float)res_w; + + float v0 = values_getter(data, (0 + values_offset) % values_count); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) ); // Point in the normalized space of our target rectangle + + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); + + for (int n = 0; n < res_w; n++) + { + const float t1 = t0 + t_step; + const int v1_idx = (int)(t0 * item_count + 0.5f); + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); + const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) ); + + // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f)); + if (plot_type == ImGuiPlotType_Lines) + { + window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + else if (plot_type == ImGuiPlotType_Histogram) + { + if (pos1.x >= pos0.x + 2.0f) + pos1.x -= 1.0f; + window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + + t0 = t1; + tp0 = tp1; + } + } + + // Text overlay + if (overlay_text) + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); +} + +struct ImGuiPlotArrayGetterData +{ + const float* Values; + int Stride; + + ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } +}; + +static float Plot_ArrayGetter(void* data, int idx) +{ + ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; + const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); + return v; +} + +void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size +void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, NULL)) + return; + + // Render + fraction = ImSaturate(fraction); + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Reduce(ImVec2(window->BorderSize, window->BorderSize)); + const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); + RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding); + + // Default displaying the fraction as percentage string, but user can override it + char overlay_buf[32]; + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); + overlay = overlay_buf; + } + + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); +} + +bool ImGui::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); + } + + if (!ItemAdd(total_bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + *v = !(*v); + + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + if (*v) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding); + } + + if (g.LogEnabled) + LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]"); + if (label_size.x > 0.0f) + RenderText(text_bb.GetTL(), label); + + return pressed; +} + +bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) +{ + bool v = ((*flags & flags_value) == flags_value); + bool pressed = Checkbox(label, &v); + if (pressed) + { + if (v) + *flags |= flags_value; + else + *flags &= ~flags_value; + } + + return pressed; +} + +bool ImGui::RadioButton(const char* label, bool active) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb.Add(text_bb); + } + + if (!ItemAdd(total_bb, &id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = (float)(int)center.x + 0.5f; + center.y = (float)(int)center.y + 0.5f; + const float radius = check_bb.GetHeight() * 0.5f; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + if (active) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); + } + + if (window->Flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16); + } + + if (g.LogEnabled) + LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(text_bb.GetTL(), label); + + return pressed; +} + +bool ImGui::RadioButton(const char* label, int* v, int v_button) +{ + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + { + *v = v_button; + } + return pressed; +} + +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) +{ + int line_count = 0; + const char* s = text_begin; + while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding + if (c == '\n') + line_count++; + s--; + if (s[0] != '\n' && s[0] != '\r') + line_count++; + *out_text_end = s; + return line_count; +} + +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +{ + ImFont* font = GImGui->Font; + const float line_height = GImGui->FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const ImWchar* s = text_begin; + while (s < text_end) + { + unsigned int c = (unsigned int)(*s++); + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = font->GetCharAdvance((unsigned short)c) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (out_offset) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) +namespace ImGuiStb +{ + +static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } +static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } +static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } +static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +{ + const ImWchar* text = obj->Text.Data; + const ImWchar* text_remaining = NULL; + const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = (int)(text_remaining - (text + line_start_idx)); +} + +static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +#ifdef __APPLE__ // FIXME: Move setting to IO structure +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#endif +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->Text.Data + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text + const ImWchar* src = obj->Text.Data + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + +static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +{ + const int text_len = obj->CurLenW; + IM_ASSERT(pos <= text_len); + if (new_text_len + text_len + 1 > obj->Text.Size) + return false; + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) + return false; + + ImWchar* text = obj->Text.Data; + if (pos != text_len) + memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); + memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->Text[obj->CurLenW] = '\0'; + + return true; +} + +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x20000 + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" + +} + +void ImGuiTextEditState::OnKeyPressed(int key) +{ + stb_textedit_key(this, &StbState, key); + CursorFollow = true; + CursorAnimReset(); +} + +// Public API to manipulate UTF-8 text +// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) +// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. +void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) +{ + IM_ASSERT(pos + bytes_count <= BufTextLen); + char* dst = Buf + pos; + const char* src = Buf + pos + bytes_count; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; + + if (CursorPos + bytes_count >= pos) + CursorPos -= bytes_count; + else if (CursorPos >= pos) + CursorPos = pos; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen -= bytes_count; +} + +void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +{ + const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); + if (new_text_len + BufTextLen + 1 >= BufSize) + return; + + if (BufTextLen != pos) + memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); + memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); + Buf[BufTextLen + new_text_len] = '\0'; + + if (CursorPos >= pos) + CursorPos += new_text_len; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen += new_text_len; +} + +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + unsigned int c = *p_char; + + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) + { + bool pass = false; + pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); + pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); + if (!pass) + return false; + } + + if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. + return false; + + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsSpace(c)) + return false; + } + + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Edit a string of text +// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. +// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 +bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + + ImGuiContext& g = *GImGui; + const ImGuiIO& io = g.IO; + const ImGuiStyle& style = g.Style; + + const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + + if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn + BeginGroup(); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); + + ImGuiWindow* draw_window = window; + if (is_multiline) + { + if (!BeginChildFrame(id, frame_bb.GetSize())) + { + EndChildFrame(); + EndGroup(); + return false; + } + draw_window = GetCurrentWindow(); + size.x -= draw_window->ScrollbarSizes.x; + } + else + { + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) + return false; + } + + // Password pushes a temporary font with only a fallback glyph + if (is_password) + { + const ImFont::Glyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->DisplayOffset = g.Font->DisplayOffset; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackXAdvance = glyph->XAdvance; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } + + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiTextEditState& edit_state = g.InputTextState; + + const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + { + SetHoveredID(id); + g.MouseCursor = ImGuiMouseCursor_TextInput; + } + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); + + bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; + if (focus_requested || user_clicked || user_scrolled) + { + if (g.ActiveId != id) + { + // Start edition + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int prev_len_w = edit_state.CurLenW; + edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + edit_state.CursorAnimReset(); + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). + const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + edit_state.CursorClamp(); + } + else + { + edit_state.Id = id; + edit_state.ScrollX = 0.0f; + stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); + if (!is_multiline && focus_requested_by_code) + select_all = true; + } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + edit_state.StbState.insert_mode = true; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + SetActiveID(id, window); + FocusWindow(window); + } + else if (io.MouseClicked[0]) + { + // Release focus when we click outside + if (g.ActiveId == id) + SetActiveID(0); + } + + bool value_changed = false; + bool enter_pressed = false; + + if (g.ActiveId == id) + { + if (!is_editable && !g.ActiveIdIsJustActivated) + { + // When read-only we always use the live data passed to the function + edit_state.Text.resize(buf_size+1); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); + edit_state.CursorClamp(); + } + + edit_state.BufSizeA = buf_size; + + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner library-wide concept of Selected vs Active. + g.ActiveIdAllowOverlap = !io.MouseDown[0]; + + // Edit in progress + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); + + const bool osx_double_click_selects_words = io.OSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text + if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0])) + { + edit_state.SelectAll(); + edit_state.SelectedAllMouseLock = true; + } + else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0]) + { + // Select a word only, OS X style (by simulating keystrokes) + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } + else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) + { + stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + } + else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + { + stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + edit_state.CursorFollow = true; + } + if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) + edit_state.SelectedAllMouseLock = false; + + if (io.InputCharacters[0]) + { + // Process text input (before we check for Return because using some IME will effectively send a Return?) + // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters. + if (!(io.KeyCtrl && !io.KeyAlt) && is_editable) + { + for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) + if (unsigned int c = (unsigned int)io.InputCharacters[n]) + { + // Insert character if they pass filtering + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; + edit_state.OnKeyPressed((int)c); + } + } + + // Consume characters + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + } + + // Handle various key-presses + bool cancel_edit = false; + const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl + const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) + { + if (!edit_state.HasSelection()) + { + if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (io.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + } + edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + } + else if (IsKeyPressedMap(ImGuiKey_Enter)) + { + bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; + if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + { + SetActiveID(0); + enter_pressed = true; + } + else if (is_editable) + { + unsigned int c = '\n'; // Insert new line + if (InputTextFilterCharacter(&c, flags, callback, user_data)) + edit_state.OnKeyPressed((int)c); + } + } + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&c, flags, callback, user_data)) + edit_state.OnKeyPressed((int)c); + } + else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } + else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) + { + // Cut, Copy + const bool cut = IsKeyPressedMap(ImGuiKey_X); + if (cut && !edit_state.HasSelection()) + edit_state.SelectAll(); + + if (io.SetClipboardTextFn) + { + const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; + const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; + edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1); + ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie); + SetClipboardText(edit_state.TempTextBuffer.Data); + } + + if (cut) + { + edit_state.CursorFollow = true; + stb_textedit_cut(&edit_state, &edit_state.StbState); + } + } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable) + { + // Paste + if (const char* clipboard = GetClipboardText()) + { + // Filter pasted buffer + const int clipboard_len = (int)strlen(clipboard); + ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); + int clipboard_filtered_len = 0; + for (const char* s = clipboard; *s; ) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) + break; + if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; + clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; + } + clipboard_filtered[clipboard_filtered_len] = 0; + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + { + stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); + edit_state.CursorFollow = true; + } + ImGui::MemFree(clipboard_filtered); + } + } + + if (cancel_edit) + { + // Restore initial value + if (is_editable) + { + ImStrncpy(buf, edit_state.InitialText.Data, buf_size); + value_changed = true; + } + } + else + { + // Apply new value immediately - copy modified buffer back + // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer + // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. + // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. + if (is_editable) + { + edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); + ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); + } + + // User callback + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) + { + IM_ASSERT(callback != NULL); + + // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; + ImGuiKey event_key = ImGuiKey_COUNT; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; + event_key = ImGuiKey_Tab; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_UpArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_DownArrow; + } + else if (flags & ImGuiInputTextFlags_CallbackAlways) + event_flag = ImGuiInputTextFlags_CallbackAlways; + + if (event_flag) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = user_data; + callback_data.ReadOnly = !is_editable; + + callback_data.EventKey = event_key; + callback_data.Buf = edit_state.TempTextBuffer.Data; + callback_data.BufTextLen = edit_state.CurLenA; + callback_data.BufSize = edit_state.BufSizeA; + callback_data.BufDirty = false; + + // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) + ImWchar* text = edit_state.Text.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); + + // Call user code + callback(&callback_data); + + // Read back what user may have modified + IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); + IM_ASSERT(callback_data.Flags == flags); + if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); + if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); + if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); + if (callback_data.BufDirty) + { + IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); + edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + edit_state.CursorAnimReset(); + } + } + } + + // Copy back to user buffer + if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) + { + ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); + value_changed = true; + } + } + } + + // Render + // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. + const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; + + if (!is_multiline) + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.f, 0.f); + const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); + if (g.ActiveId == id || is_currently_scrolling) + { + edit_state.CursorAnim += io.DeltaTime; + + // This is going to be messy. We need to: + // - Display the text (this alone can be more easily clipped) + // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) + // - Measure text height (for scrollbar) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. + const ImWchar* text_begin = edit_state.Text.Data; + ImVec2 cursor_offset, select_start_offset; + + { + // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. + const ImWchar* searches_input_ptr[2]; + searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; + searches_input_ptr[1] = NULL; + int searches_remaining = 1; + int searches_result_line_number[2] = { -1, -999 }; + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + searches_result_line_number[1] = -1; + searches_remaining++; + } + + // Iterate all lines to find our line numbers + // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. + searches_remaining += is_multiline ? 1 : 0; + int line_count = 0; + for (const ImWchar* s = text_begin; *s != 0; s++) + if (*s == '\n') + { + line_count++; + if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } + } + line_count++; + if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; + if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; + + // Calculate 2d position by finding the beginning of the line and measuring distance + cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.y = searches_result_line_number[0] * g.FontSize; + if (searches_result_line_number[1] >= 0) + { + select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.y = searches_result_line_number[1] * g.FontSize; + } + + // Calculate text height + if (is_multiline) + text_size = ImVec2(size.x, line_count * g.FontSize); + } + + // Scroll + if (edit_state.CursorFollow) + { + // Horizontal scroll in chunks of quarter width + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + { + const float scroll_increment_x = size.x * 0.25f; + if (cursor_offset.x < edit_state.ScrollX) + edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= edit_state.ScrollX) + edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + } + else + { + edit_state.ScrollX = 0.0f; + } + + // Vertical scroll + if (is_multiline) + { + float scroll_y = draw_window->Scroll.y; + if (cursor_offset.y - g.FontSize < scroll_y) + scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + else if (cursor_offset.y - size.y >= scroll_y) + scroll_y = cursor_offset.y - size.y; + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag + draw_window->Scroll.y = scroll_y; + render_pos.y = draw_window->DC.CursorPos.y; + } + } + edit_state.CursorFollow = false; + const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); + + // Draw selection + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); + + float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); + ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; + for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) + { + if (rect_pos.y > clip_rect.w + g.FontSize) + break; + if (rect_pos.y < clip_rect.y) + { + while (p < text_selected_end) + if (*p++ == '\n') + break; + } + else + { + ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines + ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); + rect.Clip(clip_rect); + if (rect.Overlaps(clip_rect)) + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + } + rect_pos.x = render_pos.x - render_scroll.x; + rect_pos.y += g.FontSize; + } + } + + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); + + // Draw blinking cursor + bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (is_editable) + g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + } + else + { + // Render text only + const char* buf_end = NULL; + if (is_multiline) + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + + if (is_multiline) + { + Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line + EndChildFrame(); + EndGroup(); + } + + if (is_password) + PopFont(); + + // Log as text + if (g.LogEnabled && !is_password) + LogRenderedText(render_pos, buf_display, NULL); + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) + return enter_pressed; + else + return value_changed; +} + +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); +} + +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument) +bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + BeginGroup(); + PushID(label); + const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f; + if (step_ptr) + PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2)); + + char buf[64]; + DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf)); + + bool value_changed = false; + if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal)) + extra_flags |= ImGuiInputTextFlags_CharsDecimal; + extra_flags |= ImGuiInputTextFlags_AutoSelectAll; + if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format); + + // Step buttons + if (step_ptr) + { + PopItemWidth(); + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + value_changed = true; + } + } + PopID(); + + if (label_size.x > 0) + { + SameLine(0, style.ItemInnerSpacing.x); + RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label); + ItemSize(label_size, style.FramePadding.y); + } + EndGroup(); + + return value_changed; +} + +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char display_format[16]; + if (decimal_precision < 0) + strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1 + else + ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision); + return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags); +} + +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) +{ + // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. + const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags); +} + +bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 2, decimal_precision, extra_flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 3, decimal_precision, extra_flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 4, decimal_precision, extra_flags); +} + +bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 2, extra_flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 3, extra_flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 4, extra_flags); +} + +static bool Items_ArrayGetter(void* data, int idx, const char** out_text) +{ + const char** items = (const char**)data; + if (out_text) + *out_text = items[idx]; + return true; +} + +static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) +{ + // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. + const char* items_separated_by_zeros = (const char*)data; + int items_count = 0; + const char* p = items_separated_by_zeros; + while (*p) + { + if (idx == items_count) + break; + p += strlen(p) + 1; + items_count++; + } + if (!*p) + return false; + if (out_text) + *out_text = p; + return true; +} + +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + +// Combo box helper allowing to pass all items in a single string. +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) +{ + int items_count = 0; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open + while (*p) + { + p += strlen(p) + 1; + items_count++; + } + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); + return value_changed; +} + +// Combo box function. +bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) + return false; + + const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); + const bool hovered = IsHovered(frame_bb, id); + bool popup_open = IsPopupOpen(id); + bool popup_opened_now = false; + + const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING + RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true); + + if (*current_item >= 0 && *current_item < items_count) + { + const char* item_text; + if (items_getter(data, *current_item, &item_text)) + RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f)); + } + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if (hovered) + { + SetHoveredID(id); + if (g.IO.MouseClicked[0]) + { + SetActiveID(0); + if (IsPopupOpen(id)) + { + ClosePopup(id); + } + else + { + FocusWindow(window); + OpenPopup(label); + popup_open = popup_opened_now = true; + } + } + } + + bool value_changed = false; + if (IsPopupOpen(id)) + { + // Size default to hold ~7 items + if (height_in_items < 0) + height_in_items = 7; + + float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3); + float popup_y1 = frame_bb.Max.y; + float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y); + if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y)) + { + // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement) + popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y); + popup_y2 = frame_bb.Min.y; + } + ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2)); + SetNextWindowPos(popup_rect.Min); + SetNextWindowSize(popup_rect.GetSize()); + PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); + + const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0); + if (BeginPopupEx(label, flags)) + { + // Display items + Spacing(); + for (int i = 0; i < items_count; i++) + { + PushID((void*)(intptr_t)i); + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + if (Selectable(item_text, item_selected)) + { + SetActiveID(0); + value_changed = true; + *current_item = i; + } + if (item_selected && popup_opened_now) + SetScrollHere(); + PopID(); + } + EndPopup(); + } + PopStyleVar(); + } + return value_changed; +} + +// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. +bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + PopClipRect(); + + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrentLineTextBaseOffset; + ImRect bb(pos, pos + size); + ItemSize(bb); + + // Fill horizontal space. + ImVec2 window_padding = window->WindowPadding; + float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; + float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); + ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); + ImRect bb_with_spacing(pos, pos + size_draw); + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) + bb_with_spacing.Max.x += window_padding.x; + + // Selectables are tightly packed together, we extend the box to cover spacing between selectable. + float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); + float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); + float spacing_R = style.ItemSpacing.x - spacing_L; + float spacing_D = style.ItemSpacing.y - spacing_U; + bb_with_spacing.Min.x -= spacing_L; + bb_with_spacing.Min.y -= spacing_U; + bb_with_spacing.Max.x += spacing_R; + bb_with_spacing.Max.y += spacing_D; + if (!ItemAdd(bb_with_spacing, &id)) + { + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + PushColumnClipRect(); + return false; + } + + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick; + if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease; + if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; + if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; + bool hovered, held; + bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags); + if (flags & ImGuiSelectableFlags_Disabled) + selected = false; + + // Render + if (hovered || selected) + { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); + } + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + { + PushColumnClipRect(); + bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x); + } + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + CloseCurrentPopup(); + return pressed; +} + +bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + if (Selectable(label, *p_selected, flags, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} + +// Helper to calculate the size of a listbox and display a label on the right. +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" +bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = GetStyle(); + const ImGuiID id = GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); + ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemRect = bb; + + BeginGroup(); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + BeginChildFrame(id, frame_bb.GetSize()); + return true; +} + +bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) +{ + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); + + // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + ImVec2 size; + size.x = 0.0f; + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; + return ListBoxHeader(label, size); +} + +void ImGui::ListBoxFooter() +{ + ImGuiWindow* parent_window = GetParentWindow(); + const ImRect bb = parent_window->DC.LastItemRect; + const ImGuiStyle& style = GetStyle(); + + EndChildFrame(); + + // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) + // We call SameLine() to restore DC.CurrentLine* data + SameLine(); + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, style.FramePadding.y); + EndGroup(); +} + +bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + if (!ListBoxHeader(label, items_count, height_in_items)) + return false; + + // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. + bool value_changed = false; + ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + + PushID(i); + if (Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + PopID(); + } + ListBoxFooter(); + return value_changed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); + float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + + bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f)); + if (shortcut_size.x > 0.0f) + { + PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + + if (selected) + RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled)); + + return pressed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) +{ + if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} + +bool ImGui::BeginMainMenuBar() +{ + ImGuiContext& g = *GImGui; + SetNextWindowPos(ImVec2(0.0f, 0.0f)); + SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f)); + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); + if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar) + || !BeginMenuBar()) + { + End(); + PopStyleVar(2); + return false; + } + g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x; + return true; +} + +void ImGui::EndMainMenuBar() +{ + EndMenuBar(); + End(); + PopStyleVar(2); +} + +bool ImGui::BeginMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!(window->Flags & ImGuiWindowFlags_MenuBar)) + return false; + + IM_ASSERT(!window->DC.MenuBarAppending); + BeginGroup(); // Save position + PushID("##menubar"); + ImRect rect = window->MenuBarRect(); + PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false); + window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.MenuBarAppending = true; + AlignFirstTextHeightToWidgets(); + return true; +} + +void ImGui::EndMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + PopClipRect(); + PopID(); + window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x; + window->DC.GroupStack.back().AdvanceCursor = false; + EndGroup(); + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.MenuBarAppending = false; +} + +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImGuiWindow* backed_focused_window = g.FocusedWindow; + + bool pressed; + bool menu_is_open = IsPopupOpen(id); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); + if (menuset_is_open) + g.FocusedWindow = window; + + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); + float w = label_size.x; + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + PopStyleVar(); + SameLine(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + } + else + { + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false); + if (!enabled) PopStyleColor(); + } + + bool hovered = enabled && IsHovered(window->DC.LastItemRect, id); + if (menuset_is_open) + g.FocusedWindow = backed_focused_window; + + bool want_open = false, want_close = false; + if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_within_opened_triangle = false; + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window) + { + if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) + { + ImRect next_window_rect = next_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc); + //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug + } + } + + want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); + want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); + } + else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others + want_open = true; + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && IsPopupOpen(id)) + ClosePopupToLevel(GImGui->CurrentPopupStack.Size); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) + { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) + OpenPopup(label); + + if (menu_is_open) + { + SetNextWindowPos(popup_pos, ImGuiSetCond_Always); + ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); + menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + + return menu_is_open; +} + +void ImGui::EndMenu() +{ + EndPopup(); +} + +// A little colored square. Return true when clicked. +// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. +bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID("#colorbutton"); + const float square_size = g.FontSize; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); + ItemSize(bb, small_height ? 0.0f : style.FramePadding.y); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); + + if (hovered) + SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.z)); + + return pressed; +} + +bool ImGui::ColorEdit3(const char* label, float col[3]) +{ + float col4[4]; + col4[0] = col[0]; + col4[1] = col[1]; + col4[2] = col[2]; + col4[3] = 1.0f; + const bool value_changed = ColorEdit4(label, col4, false); + col[0] = col4[0]; + col[1] = col4[1]; + col[2] = col4[2]; + return value_changed; +} + +// Edit colors components (each component in 0.0f..1.0f range +// Use CTRL-Click to input value and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w_full = CalcItemWidth(); + const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f); + + ImGuiColorEditMode edit_mode = window->DC.ColorEditMode; + if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton) + edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3; + + float f[4] = { col[0], col[1], col[2], col[3] }; + if (edit_mode == ImGuiColorEditMode_HSV) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; + + int components = alpha ? 4 : 3; + bool value_changed = false; + + BeginGroup(); + PushID(label); + + const bool hsv = (edit_mode == 1); + switch (edit_mode) + { + case ImGuiColorEditMode_RGB: + case ImGuiColorEditMode_HSV: + { + // RGB/HSV 0..255 Sliders + const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x); + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x); + const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + const char* fmt_table[3][4] = + { + { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, + { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, + { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } + }; + const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1]; + + PushItemWidth(w_item_one); + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + if (n + 1 == components) + PushItemWidth(w_item_last); + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]); + } + PopItemWidth(); + PopItemWidth(); + } + break; + case ImGuiColorEditMode_HEX: + { + // RGB Hexadecimal Input + const float w_slider_all = w_full - square_sz; + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]); + PushItemWidth(w_slider_all - style.ItemInnerSpacing.x); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + value_changed |= true; + char* p = buf; + while (*p == '#' || ImCharIsSpace(*p)) + p++; + i[0] = i[1] = i[2] = i[3] = 0; + if (alpha) + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + } + PopItemWidth(); + } + break; + } + + SameLine(0, style.ItemInnerSpacing.x); + + const ImVec4 col_display(col[0], col[1], col[2], 1.0f); + if (ColorButton(col_display)) + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + + // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here + if (IsItemHovered()) + SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3])); + + if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) + { + SameLine(0, style.ItemInnerSpacing.x); + const char* button_titles[3] = { "RGB", "HSV", "HEX" }; + if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups)) + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + } + + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } + + // Convert back + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if (edit_mode == 1) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + + if (value_changed) + { + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + + PopID(); + EndGroup(); + + return value_changed; +} + +void ImGui::ColorEditMode(ImGuiColorEditMode mode) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ColorEditMode = mode; +} + +// Horizontal separating line. +void ImGui::Separator() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + if (window->DC.ColumnsCount > 1) + PopClipRect(); + + float x1 = window->Pos.x; + float x2 = window->Pos.x + window->Size.x; + if (!window->DC.GroupStack.empty()) + x1 += window->DC.IndentX; + + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); + ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. + if (!ItemAdd(bb, NULL)) + { + if (window->DC.ColumnsCount > 1) + PushColumnClipRect(); + return; + } + + window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Border)); + + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + LogText(IM_NEWLINE "--------------------------------"); + + if (window->DC.ColumnsCount > 1) + { + PushColumnClipRect(); + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; + } +} + +void ImGui::Spacing() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ItemSize(ImVec2(0,0)); +} + +void ImGui::Dummy(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + ItemAdd(bb, NULL); +} + +bool ImGui::IsRectVisible(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); +} + +bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); +} + +// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) +void ImGui::BeginGroup() +{ + ImGuiWindow* window = GetCurrentWindow(); + + window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + group_data.BackupCursorPos = window->DC.CursorPos; + group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; + group_data.BackupIndentX = window->DC.IndentX; + group_data.BackupGroupOffsetX = window->DC.GroupOffsetX; + group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; + group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; + group_data.BackupLogLinePosY = window->DC.LogLinePosY; + group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive; + group_data.AdvanceCursor = true; + + window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX; + window->DC.IndentX = window->DC.GroupOffsetX; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.CurrentLineHeight = 0.0f; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; +} + +void ImGui::EndGroup() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + + ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); + group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves. + group_bb.Max = ImMax(group_bb.Min, group_bb.Max); + + window->DC.CursorPos = group_data.BackupCursorPos; + window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); + window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; + window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; + window->DC.IndentX = group_data.BackupIndentX; + window->DC.GroupOffsetX = group_data.BackupGroupOffsetX; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + + if (group_data.AdvanceCursor) + { + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. + ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); + ItemAdd(group_bb, NULL); + } + + // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group. + // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context. + const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow); + if (active_id_within_group) + window->DC.LastItemId = g.ActiveId; + if (active_id_within_group && g.HoveredId == g.ActiveId) + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true; + + window->DC.GroupStack.pop_back(); + + //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // Debug +} + +// Gets back to previous line and continue with horizontal layout +// pos_x == 0 : follow right after previous item +// pos_x != 0 : align to specified x position (relative to window/group left) +// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 +// spacing_w >= 0 : enforce spacing amount +void ImGui::SameLine(float pos_x, float spacing_w) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + if (pos_x != 0.0f) + { + if (spacing_w < 0.0f) spacing_w = 0.0f; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX; + window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; + } + else + { + if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; + window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; + window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; + } + window->DC.CurrentLineHeight = window->DC.PrevLineHeight; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; +} + +void ImGui::NewLine() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. + ItemSize(ImVec2(0,0)); + else + ItemSize(ImVec2(0.0f, GImGui->FontSize)); +} + +void ImGui::NextColumn() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems || window->DC.ColumnsCount <= 1) + return; + + ImGuiContext& g = *GImGui; + PopItemWidth(); + PopClipRect(); + + window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); + if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount) + { + // Columns 1+ cancel out IndentX + window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x; + window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent); + } + else + { + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY; + window->DrawList->ChannelsSetCurrent(0); + } + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + window->DC.CursorPos.y = window->DC.ColumnsCellMinY; + window->DC.CurrentLineHeight = 0.0f; + window->DC.CurrentLineTextBaseOffset = 0.0f; + + PushColumnClipRect(); + PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup +} + +int ImGui::GetColumnIndex() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.ColumnsCurrent; +} + +int ImGui::GetColumnsCount() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.ColumnsCount; +} + +static float GetDraggedColumnOffset(int column_index) +{ + // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing + // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindowRead(); + IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets. + IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index)); + + float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x; + x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing); + + return (float)(int)x; +} + +float ImGui::GetColumnOffset(int column_index) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + if (g.ActiveId) + { + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + if (g.ActiveId == column_id) + return GetDraggedColumnOffset(column_index); + } + + IM_ASSERT(column_index < window->DC.ColumnsData.Size); + const float t = window->DC.ColumnsData[column_index].OffsetNorm; + const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX); + return (float)(int)x_offset; +} + +void ImGui::SetColumnOffset(int column_index, float offset) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + IM_ASSERT(column_index < window->DC.ColumnsData.Size); + const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX); + window->DC.ColumnsData[column_index].OffsetNorm = t; + + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + window->DC.StateStorage->SetFloat(column_id, t); +} + +float ImGui::GetColumnWidth(int column_index) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index); + return w; +} + +static void PushColumnClipRect(int column_index) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f); + float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f); + ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true); +} + +void ImGui::Columns(int columns_count, const char* id, bool border) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(columns_count >= 1); + + if (window->DC.ColumnsCount != 1) + { + if (window->DC.ColumnsCurrent != 0) + ItemSize(ImVec2(0,0)); // Advance to column 0 + PopItemWidth(); + PopClipRect(); + window->DrawList->ChannelsMerge(); + + window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); + window->DC.CursorPos.y = window->DC.ColumnsCellMaxY; + } + + // Draw columns borders and handle resize at the time of "closing" a columns set + if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems) + { + const float y1 = window->DC.ColumnsStartPosY; + const float y2 = window->DC.CursorPos.y; + for (int i = 1; i < window->DC.ColumnsCount; i++) + { + float x = window->Pos.x + GetColumnOffset(i); + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i); + const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); + if (IsClippedEx(column_rect, &column_id, false)) + continue; + + bool hovered, held; + ButtonBehavior(column_rect, column_id, &hovered, &held); + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeEW; + + // Draw before resize so our items positioning are in sync with the line being drawn + const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column); + const float xi = (float)(int)x; + window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col); + + if (held) + { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking) + x = GetDraggedColumnOffset(i); + SetColumnOffset(i, x); + } + } + } + + // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. + // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. + PushID(0x11223347 + (id ? 0 : columns_count)); + window->DC.ColumnsSetId = window->GetID(id ? id : "columns"); + PopID(); + + // Set state for first column + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsCount = columns_count; + window->DC.ColumnsShowBorders = border; + + const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x; + window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range + window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; + window->DC.ColumnsStartPosY = window->DC.CursorPos.y; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + + if (window->DC.ColumnsCount != 1) + { + // Cache column offsets + window->DC.ColumnsData.resize(columns_count + 1); + for (int column_index = 0; column_index < columns_count + 1; column_index++) + { + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + KeepAliveID(column_id); + const float default_t = column_index / (float)window->DC.ColumnsCount; + const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store a union into the map?) + window->DC.ColumnsData[column_index].OffsetNorm = t; + } + window->DrawList->ChannelsSplit(window->DC.ColumnsCount); + PushColumnClipRect(); + PushItemWidth(GetColumnWidth() * 0.65f); + } + else + { + window->DC.ColumnsData.resize(0); + } +} + +void ImGui::Indent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; +} + +void ImGui::Unindent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; +} + +void ImGui::TreePush(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(str_id ? str_id : "#TreePush"); +} + +void ImGui::TreePush(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); +} + +void ImGui::TreePushRawID(ImGuiID id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + window->IDStack.push_back(id); +} + +void ImGui::TreePop() +{ + ImGuiWindow* window = GetCurrentWindow(); + Unindent(); + window->DC.TreeDepth--; + PopID(); +} + +void ImGui::Value(const char* prefix, bool b) +{ + Text("%s: %s", prefix, (b ? "true" : "false")); +} + +void ImGui::Value(const char* prefix, int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, unsigned int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, float v, const char* float_format) +{ + if (float_format) + { + char fmt[64]; + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + Text(fmt, prefix, v); + } + else + { + Text("%s: %.3f", prefix, v); + } +} + +// FIXME: May want to remove those helpers? +void ImGui::ValueColor(const char* prefix, const ImVec4& v) +{ + Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w); + SameLine(); + ColorButton(v, true); +} + +void ImGui::ValueColor(const char* prefix, ImU32 v) +{ + Text("%s: %08X", prefix, v); + SameLine(); + ColorButton(ColorConvertU32ToFloat4(v), true); +} + +//----------------------------------------------------------------------------- +// PLATFORM DEPENDENT HELPERS +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)) +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#endif + +// Win32 API clipboard implementation +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) + +#ifdef _MSC_VER +#pragma comment(lib, "user32") +#endif + +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + static ImVector buf_local; + buf_local.clear(); + if (!OpenClipboard(NULL)) + return NULL; + HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) + return NULL; + if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) + { + int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; + buf_local.resize(buf_len); + ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL); + } + GlobalUnlock(wbuf_handle); + CloseClipboard(); + return buf_local.Data; +} + +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + if (!OpenClipboard(NULL)) + return; + const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; + HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); + if (wbuf_handle == NULL) + return; + ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); + ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); + GlobalUnlock(wbuf_handle); + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, wbuf_handle); + CloseClipboard(); +} + +#else + +// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + return GImGui->PrivateClipboard; +} + +// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + ImGuiContext& g = *GImGui; + if (g.PrivateClipboard) + { + ImGui::MemFree(g.PrivateClipboard); + g.PrivateClipboard = NULL; + } + const char* text_end = text + strlen(text); + g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1); + memcpy(g.PrivateClipboard, text, (size_t)(text_end - text)); + g.PrivateClipboard[(int)(text_end - text)] = 0; +} + +#endif + +// Win32 API IME support (for Asian languages, etc.) +#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS) + +#include +#ifdef _MSC_VER +#pragma comment(lib, "imm32") +#endif + +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ + // Notify OS Input Method Editor of text input position + if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) + if (HIMC himc = ImmGetContext(hwnd)) + { + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + cf.dwStyle = CFS_FORCE_POSITION; + ImmSetCompositionWindow(himc, &cf); + } +} + +#else + +static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} + +#endif + +//----------------------------------------------------------------------------- +// HELP +//----------------------------------------------------------------------------- + +void ImGui::ShowMetricsWindow(bool* p_open) +{ + if (ImGui::Begin("ImGui Metrics", p_open)) + { + ImGui::Text("ImGui %s", ImGui::GetVersion()); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); + ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); + static bool show_clip_rects = true; + ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects); + ImGui::Separator(); + + struct Funcs + { + static void NodeDrawList(ImDrawList* draw_list, const char* label) + { + bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); + if (draw_list == ImGui::GetWindowDrawList()) + { + ImGui::SameLine(); + ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + if (node_open) ImGui::TreePop(); + return; + } + if (!node_open) + return; + + ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list + overlay_draw_list->PushClipRectFullScreen(); + int elem_offset = 0; + for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) + { + if (pcmd->UserCallback) + { + ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); + continue; + } + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; + bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + if (show_clip_rects && ImGui::IsItemHovered()) + { + ImRect clip_rect = pcmd->ClipRect; + ImRect vtxs_rect; + for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) + vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); + clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); + vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); + } + if (!pcmd_node_open) + continue; + ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. + while (clipper.Step()) + for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) + { + char buf[300], *buf_p = buf; + ImVec2 triangles_pos[3]; + for (int n = 0; n < 3; n++, vtx_i++) + { + ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; + triangles_pos[n] = v.pos; + buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); + } + ImGui::Selectable(buf, false); + if (ImGui::IsItemHovered()) + overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle + } + ImGui::TreePop(); + } + overlay_draw_list->PopClipRect(); + ImGui::TreePop(); + } + + static void NodeWindows(ImVector& windows, const char* label) + { + if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) + return; + for (int i = 0; i < windows.Size; i++) + Funcs::NodeWindow(windows[i], "Window"); + ImGui::TreePop(); + } + + static void NodeWindow(ImGuiWindow* window, const char* label) + { + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + return; + NodeDrawList(window->DrawList, "DrawList"); + ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y); + ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); + ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); + if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); + ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); + ImGui::TreePop(); + } + }; + + ImGuiContext& g = *GImGui; // Access private state + Funcs::NodeWindows(g.Windows, "Windows"); + if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size)) + { + for (int i = 0; i < g.RenderDrawLists[0].Size; i++) + Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) + { + for (int i = 0; i < g.OpenPopupStack.Size; i++) + { + ImGuiWindow* window = g.OpenPopupStack[i].Window; + ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Basic state")) + { + ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL"); + ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); + ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame); + ImGui::TreePop(); + } + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- + +// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. +// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github. +#ifdef IMGUI_INCLUDE_IMGUI_USER_INL +#include "imgui_user.inl" +#endif + +//----------------------------------------------------------------------------- + diff --git a/3rdparty/imgui/src/imgui_draw.cpp b/3rdparty/imgui/src/imgui_draw.cpp new file mode 100644 index 0000000..4db389a --- /dev/null +++ b/3rdparty/imgui/src/imgui_draw.cpp @@ -0,0 +1,2394 @@ +// dear imgui, v1.50 WIP +// (drawing and font code) + +// Contains implementation for +// - ImDrawList +// - ImDrawData +// - ImFontAtlas +// - ImFont +// - Default font data + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#define IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_PLACEMENT_NEW +#include "imgui_internal.h" + +#include // vsnprintf, sscanf, printf +#if !defined(alloca) +#ifdef _WIN32 +#include // alloca +#elif (defined(__FreeBSD__) || defined(FreeBSD_kernel) || defined(__DragonFly__)) && !defined(__GLIBC__) +#include // alloca. FreeBSD uses stdlib.h unless GLIBC +#else +#include // alloca +#endif +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#define snprintf _snprintf +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value +#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers +#endif + +//------------------------------------------------------------------------- +// STB libraries implementation +//------------------------------------------------------------------------- + +//#define IMGUI_STB_NAMESPACE ImGuiStb +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION + +#ifdef IMGUI_STB_NAMESPACE +namespace IMGUI_STB_NAMESPACE +{ +#endif + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] +#endif + +#define STBRP_ASSERT(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +#define STBRP_STATIC +#define STB_RECT_PACK_IMPLEMENTATION +#endif +#include "stb_rect_pack.h" + +#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x)) +#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x)) +#define STBTT_assert(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#define STBTT_STATIC +#define STB_TRUETYPE_IMPLEMENTATION +#else +#define STBTT_DEF extern +#endif +#include "stb_truetype.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#ifdef IMGUI_STB_NAMESPACE +} // namespace ImGuiStb +using namespace IMGUI_STB_NAMESPACE; +#endif + +//----------------------------------------------------------------------------- +// ImDrawList +//----------------------------------------------------------------------------- + +static const ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift + +void ImDrawList::Clear() +{ + CmdBuffer.resize(0); + IdxBuffer.resize(0); + VtxBuffer.resize(0); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.resize(0); + _TextureIdStack.resize(0); + _Path.resize(0); + _ChannelsCurrent = 0; + _ChannelsCount = 1; + // NB: Do not clear channels so our allocations are re-used after the first frame. +} + +void ImDrawList::ClearFreeMemory() +{ + CmdBuffer.clear(); + IdxBuffer.clear(); + VtxBuffer.clear(); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.clear(); + _TextureIdStack.clear(); + _Path.clear(); + _ChannelsCurrent = 0; + _ChannelsCount = 1; + for (int i = 0; i < _Channels.Size; i++) + { + if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again + _Channels[i].CmdBuffer.clear(); + _Channels[i].IdxBuffer.clear(); + } + _Channels.clear(); +} + +// Use macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug mode +#define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : GNullClipRect) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) + +void ImDrawList::AddDrawCmd() +{ + ImDrawCmd draw_cmd; + draw_cmd.ClipRect = GetCurrentClipRect(); + draw_cmd.TextureId = GetCurrentTextureId(); + + IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); + CmdBuffer.push_back(draw_cmd); +} + +void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) +{ + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL) + { + AddDrawCmd(); + current_cmd = &CmdBuffer.back(); + } + current_cmd->UserCallback = callback; + current_cmd->UserCallbackData = callback_data; + + AddDrawCmd(); // Force a new command after us (see comment below) +} + +// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. +// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. +void ImDrawList::UpdateClipRect() +{ + // If current command is used with different settings we need to add a new command + const ImVec4 curr_clip_rect = GetCurrentClipRect(); + ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) + { + AddDrawCmd(); + return; + } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; + if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL) + CmdBuffer.pop_back(); + else + curr_cmd->ClipRect = curr_clip_rect; +} + +void ImDrawList::UpdateTextureID() +{ + // If current command is used with different settings we need to add a new command + const ImTextureID curr_texture_id = GetCurrentTextureId(); + ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL) + { + AddDrawCmd(); + return; + } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; + if (prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL) + CmdBuffer.pop_back(); + else + curr_cmd->TextureId = curr_texture_id; +} + +#undef GetCurrentClipRect +#undef GetCurrentTextureId + +// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) +void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) +{ + ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); + if (intersect_with_current_clip_rect && _ClipRectStack.Size) + { + ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1]; + if (cr.x < current.x) cr.x = current.x; + if (cr.y < current.y) cr.y = current.y; + if (cr.z > current.z) cr.z = current.z; + if (cr.w > current.w) cr.w = current.w; + } + cr.z = ImMax(cr.x, cr.z); + cr.w = ImMax(cr.y, cr.w); + + _ClipRectStack.push_back(cr); + UpdateClipRect(); +} + +void ImDrawList::PushClipRectFullScreen() +{ + PushClipRect(ImVec2(GNullClipRect.x, GNullClipRect.y), ImVec2(GNullClipRect.z, GNullClipRect.w)); + //PushClipRect(GetVisibleRect()); // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiContext from here? +} + +void ImDrawList::PopClipRect() +{ + IM_ASSERT(_ClipRectStack.Size > 0); + _ClipRectStack.pop_back(); + UpdateClipRect(); +} + +void ImDrawList::PushTextureID(const ImTextureID& texture_id) +{ + _TextureIdStack.push_back(texture_id); + UpdateTextureID(); +} + +void ImDrawList::PopTextureID() +{ + IM_ASSERT(_TextureIdStack.Size > 0); + _TextureIdStack.pop_back(); + UpdateTextureID(); +} + +void ImDrawList::ChannelsSplit(int channels_count) +{ + IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1); + int old_channels_count = _Channels.Size; + if (old_channels_count < channels_count) + _Channels.resize(channels_count); + _ChannelsCount = channels_count; + + // _Channels[] (24 bytes each) hold storage that we'll swap with this->_CmdBuffer/_IdxBuffer + // The content of _Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. + // When we switch to the next channel, we'll copy _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into _CmdBuffer/_IdxBuffer + memset(&_Channels[0], 0, sizeof(ImDrawChannel)); + for (int i = 1; i < channels_count; i++) + { + if (i >= old_channels_count) + { + IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); + } + else + { + _Channels[i].CmdBuffer.resize(0); + _Channels[i].IdxBuffer.resize(0); + } + if (_Channels[i].CmdBuffer.Size == 0) + { + ImDrawCmd draw_cmd; + draw_cmd.ClipRect = _ClipRectStack.back(); + draw_cmd.TextureId = _TextureIdStack.back(); + _Channels[i].CmdBuffer.push_back(draw_cmd); + } + } +} + +void ImDrawList::ChannelsMerge() +{ + // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. + if (_ChannelsCount <= 1) + return; + + ChannelsSetCurrent(0); + if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0) + CmdBuffer.pop_back(); + + int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; + for (int i = 1; i < _ChannelsCount; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) + ch.CmdBuffer.pop_back(); + new_cmd_buffer_count += ch.CmdBuffer.Size; + new_idx_buffer_count += ch.IdxBuffer.Size; + } + CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count); + IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count); + + ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; + for (int i = 1; i < _ChannelsCount; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } + if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } + } + AddDrawCmd(); + _ChannelsCount = 1; +} + +void ImDrawList::ChannelsSetCurrent(int idx) +{ + IM_ASSERT(idx < _ChannelsCount); + if (_ChannelsCurrent == idx) return; + memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times + memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); + _ChannelsCurrent = idx; + memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer)); + memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer)); + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; +} + +// NB: this can be called with negative count for removing primitives (as long as the result does not underflow) +void ImDrawList::PrimReserve(int idx_count, int vtx_count) +{ + ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1]; + draw_cmd.ElemCount += idx_count; + + int vtx_buffer_size = VtxBuffer.Size; + VtxBuffer.resize(vtx_buffer_size + vtx_count); + _VtxWritePtr = VtxBuffer.Data + vtx_buffer_size; + + int idx_buffer_size = IdxBuffer.Size; + IdxBuffer.resize(idx_buffer_size + idx_count); + _IdxWritePtr = IdxBuffer.Data + idx_buffer_size; +} + +// Fully unrolled with inline call to keep our debug builds decently fast. +void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) +{ + ImVec2 b(c.x, a.y), d(a.x, c.y), uv(GImGui->FontTexUvWhitePixel); + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) +{ + ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y); + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) +{ + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +// TODO: Thickness anti-aliased lines cap are missing their AA fringe. +void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness, bool anti_aliased) +{ + if (points_count < 2) + return; + + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + anti_aliased &= GImGui->Style.AntiAliasedLines; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug + + int count = points_count; + if (!closed) + count = points_count-1; + + const bool thick_line = thickness > 1.0f; + if (anti_aliased) + { + // Anti-aliased stroke + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & IM_COL32(255,255,255,0); + + const int idx_count = thick_line ? count*18 : count*12; + const int vtx_count = thick_line ? points_count*4 : points_count*3; + PrimReserve(idx_count, vtx_count); + + // Temporary buffer + ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); + ImVec2* temp_points = temp_normals + points_count; + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + ImVec2 diff = points[i2] - points[i1]; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i1].x = diff.y; + temp_normals[i1].y = -diff.x; + } + if (!closed) + temp_normals[points_count-1] = temp_normals[points_count-2]; + + if (!thick_line) + { + if (!closed) + { + temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; + temp_points[1] = points[0] - temp_normals[0] * AA_SIZE; + temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; + temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; + } + + // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. + unsigned int idx1 = _VtxCurrentIdx; + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; + + // Average normals + ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE; + temp_points[i2*2+0] = points[i2] + dm; + temp_points[i2*2+1] = points[i2] - dm; + + // Add indexes + _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); + _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); + _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); + _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); + _IdxWritePtr += 12; + + idx1 = idx2; + } + + // Add vertexes + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; + _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans; + _VtxWritePtr += 3; + } + } + else + { + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) + { + temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE); + temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness); + temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness); + temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE); + temp_points[(points_count-1)*4+0] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); + temp_points[(points_count-1)*4+1] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness); + temp_points[(points_count-1)*4+2] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness); + temp_points[(points_count-1)*4+3] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); + } + + // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. + unsigned int idx1 = _VtxCurrentIdx; + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; + + // Average normals + ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); + ImVec2 dm_in = dm * half_inner_thickness; + temp_points[i2*4+0] = points[i2] + dm_out; + temp_points[i2*4+1] = points[i2] + dm_in; + temp_points[i2*4+2] = points[i2] - dm_in; + temp_points[i2*4+3] = points[i2] - dm_out; + + // Add indexes + _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); + _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+1); + _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); + _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10] = (ImDrawIdx)(idx2+0); _IdxWritePtr[11] = (ImDrawIdx)(idx2+1); + _IdxWritePtr[12] = (ImDrawIdx)(idx2+2); _IdxWritePtr[13] = (ImDrawIdx)(idx1+2); _IdxWritePtr[14] = (ImDrawIdx)(idx1+3); + _IdxWritePtr[15] = (ImDrawIdx)(idx1+3); _IdxWritePtr[16] = (ImDrawIdx)(idx2+3); _IdxWritePtr[17] = (ImDrawIdx)(idx2+2); + _IdxWritePtr += 18; + + idx1 = idx2; + } + + // Add vertexes + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = temp_points[i*4+0]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col_trans; + _VtxWritePtr[1].pos = temp_points[i*4+1]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = temp_points[i*4+2]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = temp_points[i*4+3]; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col_trans; + _VtxWritePtr += 4; + } + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Stroke + const int idx_count = count*6; + const int vtx_count = count*4; // FIXME-OPT: Not sharing edges + PrimReserve(idx_count, vtx_count); + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + const ImVec2& p1 = points[i1]; + const ImVec2& p2 = points[i2]; + ImVec2 diff = p2 - p1; + diff *= ImInvLength(diff, 1.0f); + + const float dx = diff.x * (thickness * 0.5f); + const float dy = diff.y * (thickness * 0.5f); + _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); + _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); + _IdxWritePtr += 6; + _VtxCurrentIdx += 4; + } + } +} + +void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col, bool anti_aliased) +{ + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + anti_aliased &= GImGui->Style.AntiAliasedShapes; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug + + if (anti_aliased) + { + // Anti-aliased Fill + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & IM_COL32(255,255,255,0); + const int idx_count = (points_count-2)*3 + points_count*6; + const int vtx_count = (points_count*2); + PrimReserve(idx_count, vtx_count); + + // Add indexes for fill + unsigned int vtx_inner_idx = _VtxCurrentIdx; + unsigned int vtx_outer_idx = _VtxCurrentIdx+1; + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1)); + _IdxWritePtr += 3; + } + + // Compute normals + ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + const ImVec2& p0 = points[i0]; + const ImVec2& p1 = points[i1]; + ImVec2 diff = p1 - p0; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i0].x = diff.y; + temp_normals[i0].y = -diff.x; + } + + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + // Average normals + const ImVec2& n0 = temp_normals[i0]; + const ImVec2& n1 = temp_normals[i1]; + ImVec2 dm = (n0 + n1) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE * 0.5f; + + // Add vertices + _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr += 2; + + // Add indexes for fringes + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); + _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); + _IdxWritePtr += 6; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Fill + const int idx_count = (points_count-2)*3; + const int vtx_count = points_count; + PrimReserve(idx_count, vtx_count); + for (int i = 0; i < vtx_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr++; + } + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i); + _IdxWritePtr += 3; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } +} + +void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int amin, int amax) +{ + static ImVec2 circle_vtx[12]; + static bool circle_vtx_builds = false; + const int circle_vtx_count = IM_ARRAYSIZE(circle_vtx); + if (!circle_vtx_builds) + { + for (int i = 0; i < circle_vtx_count; i++) + { + const float a = ((float)i / (float)circle_vtx_count) * 2*IM_PI; + circle_vtx[i].x = cosf(a); + circle_vtx[i].y = sinf(a); + } + circle_vtx_builds = true; + } + + if (amin > amax) return; + if (radius == 0.0f) + { + _Path.push_back(centre); + } + else + { + _Path.reserve(_Path.Size + (amax - amin + 1)); + for (int a = amin; a <= amax; a++) + { + const ImVec2& c = circle_vtx[a % circle_vtx_count]; + _Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius)); + } + } +} + +void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float amax, int num_segments) +{ + if (radius == 0.0f) + _Path.push_back(centre); + _Path.reserve(_Path.Size + (num_segments + 1)); + for (int i = 0; i <= num_segments; i++) + { + const float a = amin + ((float)i / (float)num_segments) * (amax - amin); + _Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius)); + } +} + +static void PathBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +{ + float dx = x4 - x1; + float dy = y4 - y1; + float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); + float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); + d2 = (d2 >= 0) ? d2 : -d2; + d3 = (d3 >= 0) ? d3 : -d3; + if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy)) + { + path->push_back(ImVec2(x4, y4)); + } + else if (level < 10) + { + float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f; + float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f; + float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f; + float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f; + float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f; + float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f; + + PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1); + PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1); + } +} + +void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) +{ + ImVec2 p1 = _Path.back(); + if (num_segments == 0) + { + // Auto-tessellated + PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, GImGui->Style.CurveTessellationTol, 0); + } + else + { + float t_step = 1.0f / (float)num_segments; + for (int i_step = 1; i_step <= num_segments; i_step++) + { + float t = t_step * i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t*t*t; + _Path.push_back(ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y)); + } + } +} + +void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) +{ + float r = rounding; + r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f ) - 1.0f); + r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f ) - 1.0f); + + if (r <= 0.0f || rounding_corners == 0) + { + PathLineTo(a); + PathLineTo(ImVec2(b.x,a.y)); + PathLineTo(b); + PathLineTo(ImVec2(a.x,b.y)); + } + else + { + const float r0 = (rounding_corners & 1) ? r : 0.0f; + const float r1 = (rounding_corners & 2) ? r : 0.0f; + const float r2 = (rounding_corners & 4) ? r : 0.0f; + const float r3 = (rounding_corners & 8) ? r : 0.0f; + PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 6, 9); + PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 9, 12); + PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 0, 3); + PathArcToFast(ImVec2(a.x+r3,b.y-r3), r3, 3, 6); + } +} + +void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathLineTo(a + ImVec2(0.5f,0.5f)); + PathLineTo(b + ImVec2(0.5f,0.5f)); + PathStroke(col, false, thickness); +} + +// a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly. +void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.5f,0.5f), rounding, rounding_corners_flags); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + if (rounding > 0.0f) + { + PathRect(a, b, rounding, rounding_corners_flags); + PathFill(col); + } + else + { + PrimReserve(6, 4); + PrimRect(a, b, col); + } +} + +void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) +{ + if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0) + return; + + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + PrimReserve(6, 4); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3)); + PrimWriteVtx(a, uv, col_upr_left); + PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right); + PrimWriteVtx(c, uv, col_bot_right); + PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left); +} + +void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathFill(col); +} + +void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathFill(col); +} + +void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathFill(col); +} + +void ImDrawList::AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(pos0); + PathBezierCurveTo(cp0, cp1, pos1, num_segments); + PathStroke(col, false, thickness); +} + +void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + if (text_end == NULL) + text_end = text_begin + strlen(text_begin); + if (text_begin == text_end) + return; + + // Note: This is one of the few instance of breaking the encapsulation of ImDrawList, as we pull this from ImGui state, but it is just SO useful. + // Might just move Font/FontSize to ImDrawList? + if (font == NULL) + font = GImGui->Font; + if (font_size == 0.0f) + font_size = GImGui->FontSize; + + IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + ImVec4 clip_rect = _ClipRectStack.back(); + if (cpu_fine_clip_rect) + { + clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); + clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); + clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); + clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); + } + font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); +} + +void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) +{ + AddText(GImGui->Font, GImGui->FontSize, pos, col, text_begin, text_end); +} + +void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + // FIXME-OPT: This is wasting draw calls. + const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); + if (push_texture_id) + PushTextureID(user_texture_id); + + PrimReserve(6, 4); + PrimRectUV(a, b, uv0, uv1, col); + + if (push_texture_id) + PopTextureID(); +} + +//----------------------------------------------------------------------------- +// ImDrawData +//----------------------------------------------------------------------------- + +// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! +void ImDrawData::DeIndexAllBuffers() +{ + ImVector new_vtx_buffer; + TotalVtxCount = TotalIdxCount = 0; + for (int i = 0; i < CmdListsCount; i++) + { + ImDrawList* cmd_list = CmdLists[i]; + if (cmd_list->IdxBuffer.empty()) + continue; + new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); + for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) + new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; + cmd_list->VtxBuffer.swap(new_vtx_buffer); + cmd_list->IdxBuffer.resize(0); + TotalVtxCount += cmd_list->VtxBuffer.Size; + } +} + +// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +void ImDrawData::ScaleClipRects(const ImVec2& scale) +{ + for (int i = 0; i < CmdListsCount; i++) + { + ImDrawList* cmd_list = CmdLists[i]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; + cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y); + } + } +} + +//----------------------------------------------------------------------------- +// ImFontAtlas +//----------------------------------------------------------------------------- + +ImFontConfig::ImFontConfig() +{ + FontData = NULL; + FontDataSize = 0; + FontDataOwnedByAtlas = true; + FontNo = 0; + SizePixels = 0.0f; + OversampleH = 3; + OversampleV = 1; + PixelSnapH = false; + GlyphExtraSpacing = ImVec2(0.0f, 0.0f); + GlyphRanges = NULL; + MergeMode = false; + MergeGlyphCenterV = false; + DstFont = NULL; + memset(Name, 0, sizeof(Name)); +} + +ImFontAtlas::ImFontAtlas() +{ + TexID = NULL; + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; + TexWidth = TexHeight = TexDesiredWidth = 0; + TexUvWhitePixel = ImVec2(0, 0); +} + +ImFontAtlas::~ImFontAtlas() +{ + Clear(); +} + +void ImFontAtlas::ClearInputData() +{ + for (int i = 0; i < ConfigData.Size; i++) + if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) + { + ImGui::MemFree(ConfigData[i].FontData); + ConfigData[i].FontData = NULL; + } + + // When clearing this we lose access to the font name and other information used to build the font. + for (int i = 0; i < Fonts.Size; i++) + if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) + { + Fonts[i]->ConfigData = NULL; + Fonts[i]->ConfigDataCount = 0; + } + ConfigData.clear(); +} + +void ImFontAtlas::ClearTexData() +{ + if (TexPixelsAlpha8) + ImGui::MemFree(TexPixelsAlpha8); + if (TexPixelsRGBA32) + ImGui::MemFree(TexPixelsRGBA32); + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; +} + +void ImFontAtlas::ClearFonts() +{ + for (int i = 0; i < Fonts.Size; i++) + { + Fonts[i]->~ImFont(); + ImGui::MemFree(Fonts[i]); + } + Fonts.clear(); +} + +void ImFontAtlas::Clear() +{ + ClearInputData(); + ClearTexData(); + ClearFonts(); +} + +void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Build atlas on demand + if (TexPixelsAlpha8 == NULL) + { + if (ConfigData.empty()) + AddFontDefault(); + Build(); + } + + *out_pixels = TexPixelsAlpha8; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; +} + +void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Convert to RGBA32 format on demand + // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp + if (!TexPixelsRGBA32) + { + unsigned char* pixels; + GetTexDataAsAlpha8(&pixels, NULL, NULL); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + const unsigned char* src = pixels; + unsigned int* dst = TexPixelsRGBA32; + for (int n = TexWidth * TexHeight; n > 0; n--) + *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); + } + + *out_pixels = (unsigned char*)TexPixelsRGBA32; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; +} + +ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) +{ + IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); + IM_ASSERT(font_cfg->SizePixels > 0.0f); + + // Create new font + if (!font_cfg->MergeMode) + { + ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); + IM_PLACEMENT_NEW(font) ImFont(); + Fonts.push_back(font); + } + + ConfigData.push_back(*font_cfg); + ImFontConfig& new_font_cfg = ConfigData.back(); + if (!new_font_cfg.DstFont) + new_font_cfg.DstFont = Fonts.back(); + if (!new_font_cfg.FontDataOwnedByAtlas) + { + new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize); + new_font_cfg.FontDataOwnedByAtlas = true; + memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); + } + + // Invalidate texture + ClearTexData(); + return new_font_cfg.DstFont; +} + +// Default font TTF is compressed with stb_compress then base85 encoded (see extra_fonts/binary_to_compressed_c.cpp for encoder) +static unsigned int stb_decompress_length(unsigned char *input); +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); +static const char* GetDefaultCompressedFontDataTTFBase85(); +static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } +static void Decode85(const unsigned char* src, unsigned char* dst) +{ + while (*src) + { + unsigned int tmp = Decode85Byte(src[0]) + 85*(Decode85Byte(src[1]) + 85*(Decode85Byte(src[2]) + 85*(Decode85Byte(src[3]) + 85*Decode85Byte(src[4])))); + dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness. + src += 5; + dst += 4; + } +} + +// Load embedded ProggyClean.ttf at size 13, disable oversampling +ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) +{ + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (!font_cfg_template) + { + font_cfg.OversampleH = font_cfg.OversampleV = 1; + font_cfg.PixelSnapH = true; + } + if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "ProggyClean.ttf, 13px"); + + const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); + ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, 13.0f, &font_cfg, GetGlyphRangesDefault()); + return font; +} + +ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + int data_size = 0; + void* data = ImLoadFileToMemory(filename, "rb", &data_size, 0); + if (!data) + { + IM_ASSERT(0); // Could not load file. + return NULL; + } + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (font_cfg.Name[0] == '\0') + { + // Store a short copy of filename into into the font name for convenience + const char* p; + for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} + snprintf(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); + } + return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges); +} + +// NBM Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). +ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontData = ttf_data; + font_cfg.FontDataSize = ttf_size; + font_cfg.SizePixels = size_pixels; + if (glyph_ranges) + font_cfg.GlyphRanges = glyph_ranges; + return AddFont(&font_cfg); +} + +ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data); + unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); + stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); + + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontDataOwnedByAtlas = true; + return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); +} + +ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) +{ + int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4; + void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size); + Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); + ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); + ImGui::MemFree(compressed_ttf); + return font; +} + +bool ImFontAtlas::Build() +{ + IM_ASSERT(ConfigData.Size > 0); + + TexID = NULL; + TexWidth = TexHeight = 0; + TexUvWhitePixel = ImVec2(0, 0); + ClearTexData(); + + struct ImFontTempBuildData + { + stbtt_fontinfo FontInfo; + stbrp_rect* Rects; + stbtt_pack_range* Ranges; + int RangesCount; + }; + ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); + + // Initialize font information early (so we can error without any cleanup) + count glyphs + int total_glyph_count = 0; + int total_glyph_range_count = 0; + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); + IM_ASSERT(font_offset >= 0); + if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + return false; + + // Count glyphs + if (!cfg.GlyphRanges) + cfg.GlyphRanges = GetGlyphRangesDefault(); + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + total_glyph_count += (in_range[1] - in_range[0]) + 1; + total_glyph_range_count++; + } + } + + // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish. + // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. + TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512; + TexHeight = 0; + const int max_tex_height = 1024*32; + stbtt_pack_context spc; + stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); + + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + ImVector extra_rects; + RenderCustomTexData(0, &extra_rects); + stbtt_PackSetOversampling(&spc, 1, 1); + stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size); + for (int i = 0; i < extra_rects.Size; i++) + if (extra_rects[i].was_packed) + TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h); + + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) + int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; + stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); + stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect)); + stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range)); + memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar)); + memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. + memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); + + // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + + // Setup ranges + int glyph_count = 0; + int glyph_ranges_count = 0; + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + glyph_count += (in_range[1] - in_range[0]) + 1; + glyph_ranges_count++; + } + tmp.Ranges = buf_ranges + buf_ranges_n; + tmp.RangesCount = glyph_ranges_count; + buf_ranges_n += glyph_ranges_count; + for (int i = 0; i < glyph_ranges_count; i++) + { + const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; + stbtt_pack_range& range = tmp.Ranges[i]; + range.font_size = cfg.SizePixels; + range.first_unicode_codepoint_in_range = in_range[0]; + range.num_chars = (in_range[1] - in_range[0]) + 1; + range.chardata_for_range = buf_packedchars + buf_packedchars_n; + buf_packedchars_n += range.num_chars; + } + + // Pack + tmp.Rects = buf_rects + buf_rects_n; + buf_rects_n += glyph_count; + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); + + // Extend texture height + for (int i = 0; i < n; i++) + if (tmp.Rects[i].was_packed) + TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); + } + IM_ASSERT(buf_rects_n == total_glyph_count); + IM_ASSERT(buf_packedchars_n == total_glyph_count); + IM_ASSERT(buf_ranges_n == total_glyph_range_count); + + // Create texture + TexHeight = ImUpperPowerOfTwo(TexHeight); + TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight); + memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); + spc.pixels = TexPixelsAlpha8; + spc.height = TexHeight; + + // Second pass: render characters + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + tmp.Rects = NULL; + } + + // End packing + stbtt_PackEnd(&spc); + ImGui::MemFree(buf_rects); + buf_rects = NULL; + + // Third pass: setup ImFont and glyphs for runtime + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFont* dst_font = cfg.DstFont; + + float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); + + float ascent = unscaled_ascent * font_scale; + float descent = unscaled_descent * font_scale; + if (!cfg.MergeMode) + { + dst_font->ContainerAtlas = this; + dst_font->ConfigData = &cfg; + dst_font->ConfigDataCount = 0; + dst_font->FontSize = cfg.SizePixels; + dst_font->Ascent = ascent; + dst_font->Descent = descent; + dst_font->Glyphs.resize(0); + } + dst_font->ConfigDataCount++; + float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f; + + dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable() + for (int i = 0; i < tmp.RangesCount; i++) + { + stbtt_pack_range& range = tmp.Ranges[i]; + for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) + { + const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; + if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) + continue; + + const int codepoint = range.first_unicode_codepoint_in_range + char_idx; + if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint)) + continue; + + stbtt_aligned_quad q; + float dummy_x = 0.0f, dummy_y = 0.0f; + stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); + + dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); + ImFont::Glyph& glyph = dst_font->Glyphs.back(); + glyph.Codepoint = (ImWchar)codepoint; + glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1; + glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1; + glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance + if (cfg.PixelSnapH) + glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); + } + } + cfg.DstFont->BuildLookupTable(); + } + + // Cleanup temporaries + ImGui::MemFree(buf_packedchars); + ImGui::MemFree(buf_ranges); + ImGui::MemFree(tmp_array); + + // Render into our custom data block + RenderCustomTexData(1, &extra_rects); + + return true; +} + +void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) +{ + // A work of art lies ahead! (. = white layer, X = black layer, others are blank) + // The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. + const int TEX_DATA_W = 90; + const int TEX_DATA_H = 27; + const char texture_data[TEX_DATA_W*TEX_DATA_H+1] = + { + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " + }; + + ImVector& rects = *(ImVector*)p_rects; + if (pass == 0) + { + // Request rectangles + stbrp_rect r; + memset(&r, 0, sizeof(r)); + r.w = (TEX_DATA_W*2)+1; + r.h = TEX_DATA_H+1; + rects.push_back(r); + } + else if (pass == 1) + { + // Render/copy pixels + const stbrp_rect& r = rects[0]; + for (int y = 0, n = 0; y < TEX_DATA_H; y++) + for (int x = 0; x < TEX_DATA_W; x++, n++) + { + const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth; + const int offset1 = offset0 + 1 + TEX_DATA_W; + TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00; + TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00; + } + const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); + TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y); + + // Setup mouse cursors + const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = + { + // Pos ........ Size ......... Offset ...... + { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE + }; + + for (int type = 0; type < ImGuiMouseCursor_Count_; type++) + { + ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; + ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); + const ImVec2 size = cursor_datas[type][1]; + cursor_data.Type = type; + cursor_data.Size = size; + cursor_data.HotOffset = cursor_datas[type][2]; + cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; + pos.x += TEX_DATA_W+1; + cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; + } + } +} + +// Retrieve list of range (2 int per range, values are inclusive) +const ImWchar* ImFontAtlas::GetGlyphRangesDefault() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesKorean() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3131, 0x3163, // Korean alphabets + 0xAC00, 0xD79D, // Korean characters + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesChinese() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + 0x4e00, 0x9FAF, // CJK Ideograms + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() +{ + // Store the 1946 ideograms code points as successive offsets from the initial unicode codepoint 0x4E00. Each offset has an implicit +1. + // This encoding helps us reduce the source code size. + static const short offsets_from_0x4E00[] = + { + -1,0,1,3,0,0,0,0,1,0,5,1,1,0,7,4,6,10,0,1,9,9,7,1,3,19,1,10,7,1,0,1,0,5,1,0,6,4,2,6,0,0,12,6,8,0,3,5,0,1,0,9,0,0,8,1,1,3,4,5,13,0,0,8,2,17, + 4,3,1,1,9,6,0,0,0,2,1,3,2,22,1,9,11,1,13,1,3,12,0,5,9,2,0,6,12,5,3,12,4,1,2,16,1,1,4,6,5,3,0,6,13,15,5,12,8,14,0,0,6,15,3,6,0,18,8,1,6,14,1, + 5,4,12,24,3,13,12,10,24,0,0,0,1,0,1,1,2,9,10,2,2,0,0,3,3,1,0,3,8,0,3,2,4,4,1,6,11,10,14,6,15,3,4,15,1,0,0,5,2,2,0,0,1,6,5,5,6,0,3,6,5,0,0,1,0, + 11,2,2,8,4,7,0,10,0,1,2,17,19,3,0,2,5,0,6,2,4,4,6,1,1,11,2,0,3,1,2,1,2,10,7,6,3,16,0,8,24,0,0,3,1,1,3,0,1,6,0,0,0,2,0,1,5,15,0,1,0,0,2,11,19, + 1,4,19,7,6,5,1,0,0,0,0,5,1,0,1,9,0,0,5,0,2,0,1,0,3,0,11,3,0,2,0,0,0,0,0,9,3,6,4,12,0,14,0,0,29,10,8,0,14,37,13,0,31,16,19,0,8,30,1,20,8,3,48, + 21,1,0,12,0,10,44,34,42,54,11,18,82,0,2,1,2,12,1,0,6,2,17,2,12,7,0,7,17,4,2,6,24,23,8,23,39,2,16,23,1,0,5,1,2,15,14,5,6,2,11,0,8,6,2,2,2,14, + 20,4,15,3,4,11,10,10,2,5,2,1,30,2,1,0,0,22,5,5,0,3,1,5,4,1,0,0,2,2,21,1,5,1,2,16,2,1,3,4,0,8,4,0,0,5,14,11,2,16,1,13,1,7,0,22,15,3,1,22,7,14, + 22,19,11,24,18,46,10,20,64,45,3,2,0,4,5,0,1,4,25,1,0,0,2,10,0,0,0,1,0,1,2,0,0,9,1,2,0,0,0,2,5,2,1,1,5,5,8,1,1,1,5,1,4,9,1,3,0,1,0,1,1,2,0,0, + 2,0,1,8,22,8,1,0,0,0,0,4,2,1,0,9,8,5,0,9,1,30,24,2,6,4,39,0,14,5,16,6,26,179,0,2,1,1,0,0,0,5,2,9,6,0,2,5,16,7,5,1,1,0,2,4,4,7,15,13,14,0,0, + 3,0,1,0,0,0,2,1,6,4,5,1,4,9,0,3,1,8,0,0,10,5,0,43,0,2,6,8,4,0,2,0,0,9,6,0,9,3,1,6,20,14,6,1,4,0,7,2,3,0,2,0,5,0,3,1,0,3,9,7,0,3,4,0,4,9,1,6,0, + 9,0,0,2,3,10,9,28,3,6,2,4,1,2,32,4,1,18,2,0,3,1,5,30,10,0,2,2,2,0,7,9,8,11,10,11,7,2,13,7,5,10,0,3,40,2,0,1,6,12,0,4,5,1,5,11,11,21,4,8,3,7, + 8,8,33,5,23,0,0,19,8,8,2,3,0,6,1,1,1,5,1,27,4,2,5,0,3,5,6,3,1,0,3,1,12,5,3,3,2,0,7,7,2,1,0,4,0,1,1,2,0,10,10,6,2,5,9,7,5,15,15,21,6,11,5,20, + 4,3,5,5,2,5,0,2,1,0,1,7,28,0,9,0,5,12,5,5,18,30,0,12,3,3,21,16,25,32,9,3,14,11,24,5,66,9,1,2,0,5,9,1,5,1,8,0,8,3,3,0,1,15,1,4,8,1,2,7,0,7,2, + 8,3,7,5,3,7,10,2,1,0,0,2,25,0,6,4,0,10,0,4,2,4,1,12,5,38,4,0,4,1,10,5,9,4,0,14,4,2,5,18,20,21,1,3,0,5,0,7,0,3,7,1,3,1,1,8,1,0,0,0,3,2,5,2,11, + 6,0,13,1,3,9,1,12,0,16,6,2,1,0,2,1,12,6,13,11,2,0,28,1,7,8,14,13,8,13,0,2,0,5,4,8,10,2,37,42,19,6,6,7,4,14,11,18,14,80,7,6,0,4,72,12,36,27, + 7,7,0,14,17,19,164,27,0,5,10,7,3,13,6,14,0,2,2,5,3,0,6,13,0,0,10,29,0,4,0,3,13,0,3,1,6,51,1,5,28,2,0,8,0,20,2,4,0,25,2,10,13,10,0,16,4,0,1,0, + 2,1,7,0,1,8,11,0,0,1,2,7,2,23,11,6,6,4,16,2,2,2,0,22,9,3,3,5,2,0,15,16,21,2,9,20,15,15,5,3,9,1,0,0,1,7,7,5,4,2,2,2,38,24,14,0,0,15,5,6,24,14, + 5,5,11,0,21,12,0,3,8,4,11,1,8,0,11,27,7,2,4,9,21,59,0,1,39,3,60,62,3,0,12,11,0,3,30,11,0,13,88,4,15,5,28,13,1,4,48,17,17,4,28,32,46,0,16,0, + 18,11,1,8,6,38,11,2,6,11,38,2,0,45,3,11,2,7,8,4,30,14,17,2,1,1,65,18,12,16,4,2,45,123,12,56,33,1,4,3,4,7,0,0,0,3,2,0,16,4,2,4,2,0,7,4,5,2,26, + 2,25,6,11,6,1,16,2,6,17,77,15,3,35,0,1,0,5,1,0,38,16,6,3,12,3,3,3,0,9,3,1,3,5,2,9,0,18,0,25,1,3,32,1,72,46,6,2,7,1,3,14,17,0,28,1,40,13,0,20, + 15,40,6,38,24,12,43,1,1,9,0,12,6,0,6,2,4,19,3,7,1,48,0,9,5,0,5,6,9,6,10,15,2,11,19,3,9,2,0,1,10,1,27,8,1,3,6,1,14,0,26,0,27,16,3,4,9,6,2,23, + 9,10,5,25,2,1,6,1,1,48,15,9,15,14,3,4,26,60,29,13,37,21,1,6,4,0,2,11,22,23,16,16,2,2,1,3,0,5,1,6,4,0,0,4,0,0,8,3,0,2,5,0,7,1,7,3,13,2,4,10, + 3,0,2,31,0,18,3,0,12,10,4,1,0,7,5,7,0,5,4,12,2,22,10,4,2,15,2,8,9,0,23,2,197,51,3,1,1,4,13,4,3,21,4,19,3,10,5,40,0,4,1,1,10,4,1,27,34,7,21, + 2,17,2,9,6,4,2,3,0,4,2,7,8,2,5,1,15,21,3,4,4,2,2,17,22,1,5,22,4,26,7,0,32,1,11,42,15,4,1,2,5,0,19,3,1,8,6,0,10,1,9,2,13,30,8,2,24,17,19,1,4, + 4,25,13,0,10,16,11,39,18,8,5,30,82,1,6,8,18,77,11,13,20,75,11,112,78,33,3,0,0,60,17,84,9,1,1,12,30,10,49,5,32,158,178,5,5,6,3,3,1,3,1,4,7,6, + 19,31,21,0,2,9,5,6,27,4,9,8,1,76,18,12,1,4,0,3,3,6,3,12,2,8,30,16,2,25,1,5,5,4,3,0,6,10,2,3,1,0,5,1,19,3,0,8,1,5,2,6,0,0,0,19,1,2,0,5,1,2,5, + 1,3,7,0,4,12,7,3,10,22,0,9,5,1,0,2,20,1,1,3,23,30,3,9,9,1,4,191,14,3,15,6,8,50,0,1,0,0,4,0,0,1,0,2,4,2,0,2,3,0,2,0,2,2,8,7,0,1,1,1,3,3,17,11, + 91,1,9,3,2,13,4,24,15,41,3,13,3,1,20,4,125,29,30,1,0,4,12,2,21,4,5,5,19,11,0,13,11,86,2,18,0,7,1,8,8,2,2,22,1,2,6,5,2,0,1,2,8,0,2,0,5,2,1,0, + 2,10,2,0,5,9,2,1,2,0,1,0,4,0,0,10,2,5,3,0,6,1,0,1,4,4,33,3,13,17,3,18,6,4,7,1,5,78,0,4,1,13,7,1,8,1,0,35,27,15,3,0,0,0,1,11,5,41,38,15,22,6, + 14,14,2,1,11,6,20,63,5,8,27,7,11,2,2,40,58,23,50,54,56,293,8,8,1,5,1,14,0,1,12,37,89,8,8,8,2,10,6,0,0,0,4,5,2,1,0,1,1,2,7,0,3,3,0,4,6,0,3,2, + 19,3,8,0,0,0,4,4,16,0,4,1,5,1,3,0,3,4,6,2,17,10,10,31,6,4,3,6,10,126,7,3,2,2,0,9,0,0,5,20,13,0,15,0,6,0,2,5,8,64,50,3,2,12,2,9,0,0,11,8,20, + 109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38, + }; + static ImWchar base_ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + }; + static bool full_ranges_unpacked = false; + static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1]; + if (!full_ranges_unpacked) + { + // Unpack + int codepoint = 0x4e00; + memcpy(full_ranges, base_ranges, sizeof(base_ranges)); + ImWchar* dst = full_ranges + IM_ARRAYSIZE(base_ranges);; + for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2) + dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1)); + dst[0] = 0; + full_ranges_unpacked = true; + } + return &full_ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x0400, 0x052F, // Cyrillic + Cyrillic Supplement + 0x2DE0, 0x2DFF, // Cyrillic Extended-A + 0xA640, 0xA69F, // Cyrillic Extended-B + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesThai() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0E00, 0x0E7F, // Thai + 0, + }; + return &ranges[0]; +} + +//----------------------------------------------------------------------------- +// ImFont +//----------------------------------------------------------------------------- + +ImFont::ImFont() +{ + Scale = 1.0f; + FallbackChar = (ImWchar)'?'; + Clear(); +} + +ImFont::~ImFont() +{ + // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. + // If you want to delete fonts you need to do it between Render() and NewFrame(). + // FIXME-CLEANUP + /* + ImGuiContext& g = *GImGui; + if (g.Font == this) + g.Font = NULL; + */ + Clear(); +} + +void ImFont::Clear() +{ + FontSize = 0.0f; + DisplayOffset = ImVec2(0.0f, 1.0f); + ConfigData = NULL; + ConfigDataCount = 0; + Ascent = Descent = 0.0f; + ContainerAtlas = NULL; + Glyphs.clear(); + FallbackGlyph = NULL; + FallbackXAdvance = 0.0f; + IndexXAdvance.clear(); + IndexLookup.clear(); +} + +void ImFont::BuildLookupTable() +{ + int max_codepoint = 0; + for (int i = 0; i != Glyphs.Size; i++) + max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); + + IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved + IndexXAdvance.clear(); + IndexLookup.clear(); + GrowIndex(max_codepoint + 1); + for (int i = 0; i < Glyphs.Size; i++) + { + int codepoint = (int)Glyphs[i].Codepoint; + IndexXAdvance[codepoint] = Glyphs[i].XAdvance; + IndexLookup[codepoint] = (unsigned short)i; + } + + // Create a glyph to handle TAB + // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) + if (FindGlyph((unsigned short)' ')) + { + if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times + Glyphs.resize(Glyphs.Size + 1); + ImFont::Glyph& tab_glyph = Glyphs.back(); + tab_glyph = *FindGlyph((unsigned short)' '); + tab_glyph.Codepoint = '\t'; + tab_glyph.XAdvance *= 4; + IndexXAdvance[(int)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; + IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); + } + + FallbackGlyph = NULL; + FallbackGlyph = FindGlyph(FallbackChar); + FallbackXAdvance = FallbackGlyph ? FallbackGlyph->XAdvance : 0.0f; + for (int i = 0; i < max_codepoint + 1; i++) + if (IndexXAdvance[i] < 0.0f) + IndexXAdvance[i] = FallbackXAdvance; +} + +void ImFont::SetFallbackChar(ImWchar c) +{ + FallbackChar = c; + BuildLookupTable(); +} + +void ImFont::GrowIndex(int new_size) +{ + IM_ASSERT(IndexXAdvance.Size == IndexLookup.Size); + int old_size = IndexLookup.Size; + if (new_size <= old_size) + return; + IndexXAdvance.resize(new_size); + IndexLookup.resize(new_size); + for (int i = old_size; i < new_size; i++) + { + IndexXAdvance[i] = -1.0f; + IndexLookup[i] = (unsigned short)-1; + } +} + +void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) +{ + IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. + int index_size = IndexLookup.Size; + + if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists + return; + if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op + return; + + GrowIndex(dst + 1); + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; + IndexXAdvance[dst] = (src < index_size) ? IndexXAdvance.Data[src] : 1.0f; +} + +const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const +{ + if (c < IndexLookup.Size) + { + const unsigned short i = IndexLookup[c]; + if (i != (unsigned short)-1) + return &Glyphs.Data[i]; + } + return FallbackGlyph; +} + +const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const +{ + // Simple word-wrapping for English, not full-featured. Please submit failing cases! + // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) + + // For references, possible wrap point marked with ^ + // "aaa bbb, ccc,ddd. eee fff. ggg!" + // ^ ^ ^ ^ ^__ ^ ^ + + // List of hardcoded separators: .,;!?'" + + // Skip extra blanks after a line returns (that includes not counting them in width computation) + // e.g. "Hello world" --> "Hello" "World" + + // Cut words that cannot possibly fit within one line. + // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" + + float line_width = 0.0f; + float word_width = 0.0f; + float blank_width = 0.0f; + + const char* word_end = text; + const char* prev_word_end = NULL; + bool inside_word = true; + + const char* s = text; + while (s < text_end) + { + unsigned int c = (unsigned int)*s; + const char* next_s; + if (c < 0x80) + next_s = s + 1; + else + next_s = s + ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + + if (c < 32) + { + if (c == '\n') + { + line_width = word_width = blank_width = 0.0f; + inside_word = true; + s = next_s; + continue; + } + if (c == '\r') + { + s = next_s; + continue; + } + } + + const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance) * scale; + if (ImCharIsSpace(c)) + { + if (inside_word) + { + line_width += blank_width; + blank_width = 0.0f; + } + blank_width += char_width; + inside_word = false; + } + else + { + word_width += char_width; + if (inside_word) + { + word_end = next_s; + } + else + { + prev_word_end = word_end; + line_width += word_width + blank_width; + word_width = blank_width = 0.0f; + } + + // Allow wrapping after punctuation. + inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' || c == '?' || c == '\"'); + } + + // We ignore blank width at the end of the line (they can be skipped) + if (line_width + word_width >= wrap_width) + { + // Words that cannot possibly fit within an entire line will be cut anywhere. + if (word_width < wrap_width) + s = prev_word_end ? prev_word_end : word_end; + break; + } + + s = next_s; + } + + return s; +} + +ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const +{ + if (!text_end) + text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this. + + const float line_height = size; + const float scale = size / FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const bool word_wrap_enabled = (wrap_width > 0.0f); + const char* word_wrap_eol = NULL; + + const char* s = text_begin; + while (s < text_end) + { + if (word_wrap_enabled) + { + // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); + if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. + word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below + } + + if (s >= word_wrap_eol) + { + if (text_size.x < line_width) + text_size.x = line_width; + text_size.y += line_height; + line_width = 0.0f; + word_wrap_eol = NULL; + + // Wrapping skips upcoming blanks + while (s < text_end) + { + const char c = *s; + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + } + continue; + } + } + + // Decode and advance source + const char* prev_s = s; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) // Malformed UTF-8? + break; + } + + if (c < 32) + { + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + continue; + } + if (c == '\r') + continue; + } + + const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance) * scale; + if (line_width + char_width >= max_width) + { + s = prev_s; + break; + } + + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const +{ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. + return; + if (const Glyph* glyph = FindGlyph(c)) + { + float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; + pos.x = (float)(int)pos.x + DisplayOffset.x; + pos.y = (float)(int)pos.y + DisplayOffset.y; + ImVec2 pos_tl(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale); + ImVec2 pos_br(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale); + draw_list->PrimReserve(6, 4); + draw_list->PrimRectUV(pos_tl, pos_br, ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); + } +} + +void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const +{ + if (!text_end) + text_end = text_begin + strlen(text_begin); // ImGui functions generally already provides a valid text_end, so this is merely to handle direct calls. + + // Align to be pixel perfect + pos.x = (float)(int)pos.x + DisplayOffset.x; + pos.y = (float)(int)pos.y + DisplayOffset.y; + float x = pos.x; + float y = pos.y; + if (y > clip_rect.w) + return; + + const float scale = size / FontSize; + const float line_height = FontSize * scale; + const bool word_wrap_enabled = (wrap_width > 0.0f); + const char* word_wrap_eol = NULL; + + // Skip non-visible lines + const char* s = text_begin; + if (!word_wrap_enabled && y + line_height < clip_rect.y) + while (s < text_end && *s != '\n') // Fast-forward to next line + s++; + + // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) + const int vtx_count_max = (int)(text_end - s) * 4; + const int idx_count_max = (int)(text_end - s) * 6; + const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; + draw_list->PrimReserve(idx_count_max, vtx_count_max); + + ImDrawVert* vtx_write = draw_list->_VtxWritePtr; + ImDrawIdx* idx_write = draw_list->_IdxWritePtr; + unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; + + while (s < text_end) + { + if (word_wrap_enabled) + { + // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x)); + if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. + word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below + } + + if (s >= word_wrap_eol) + { + x = pos.x; + y += line_height; + word_wrap_eol = NULL; + + // Wrapping skips upcoming blanks + while (s < text_end) + { + const char c = *s; + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + } + continue; + } + } + + // Decode and advance source + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) // Malformed UTF-8? + break; + } + + if (c < 32) + { + if (c == '\n') + { + x = pos.x; + y += line_height; + + if (y > clip_rect.w) + break; + if (!word_wrap_enabled && y + line_height < clip_rect.y) + while (s < text_end && *s != '\n') // Fast-forward to next line + s++; + continue; + } + if (c == '\r') + continue; + } + + float char_width = 0.0f; + if (const Glyph* glyph = FindGlyph((unsigned short)c)) + { + char_width = glyph->XAdvance * scale; + + // Arbitrarily assume that both space and tabs are empty glyphs as an optimization + if (c != ' ' && c != '\t') + { + // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w + float x1 = x + glyph->X0 * scale; + float x2 = x + glyph->X1 * scale; + float y1 = y + glyph->Y0 * scale; + float y2 = y + glyph->Y1 * scale; + if (x1 <= clip_rect.z && x2 >= clip_rect.x) + { + // Render a character + float u1 = glyph->U0; + float v1 = glyph->V0; + float u2 = glyph->U1; + float v2 = glyph->V1; + + // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. + if (cpu_fine_clip) + { + if (x1 < clip_rect.x) + { + u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1); + x1 = clip_rect.x; + } + if (y1 < clip_rect.y) + { + v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1); + y1 = clip_rect.y; + } + if (x2 > clip_rect.z) + { + u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1); + x2 = clip_rect.z; + } + if (y2 > clip_rect.w) + { + v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1); + y2 = clip_rect.w; + } + if (y1 >= y2) + { + x += char_width; + continue; + } + } + + // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug build. + // Inlined here: + { + idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); + idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); + vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; + vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; + vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; + vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; + } + } + } + } + + x += char_width; + } + + // Give back unused vertices + draw_list->VtxBuffer.resize((int)(vtx_write - draw_list->VtxBuffer.Data)); + draw_list->IdxBuffer.resize((int)(idx_write - draw_list->IdxBuffer.Data)); + draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); + draw_list->_VtxWritePtr = vtx_write; + draw_list->_IdxWritePtr = idx_write; + draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size; +} + +//----------------------------------------------------------------------------- +// DEFAULT FONT DATA +//----------------------------------------------------------------------------- +// Compressed with stb_compress() then converted to a C array. +// Use the program in extra_fonts/binary_to_compressed_c.cpp to create the array from a TTF file. +// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h +//----------------------------------------------------------------------------- + +static unsigned int stb_decompress_length(unsigned char *input) +{ + return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; +} + +static unsigned char *stb__barrier, *stb__barrier2, *stb__barrier3, *stb__barrier4; +static unsigned char *stb__dout; +static void stb__match(unsigned char *data, unsigned int length) +{ + // INVERSE of memmove... write each byte before copying the next... + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(unsigned char *data, unsigned int length) +{ + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) +#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) +#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) + +static unsigned char *stb_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); + else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); + else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + } + return i; +} + +static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (stb__in4(0) != 0x57bC0000) return 0; + if (stb__in4(4) != 0) return 0; // error! stream is > 4GB + olen = stb_decompress_length(i); + stb__barrier2 = i; + stb__barrier3 = i+length; + stb__barrier = output + olen; + stb__barrier4 = output; + i += 16; + + stb__dout = output; + for (;;) { + unsigned char *old_i = i; + i = stb_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + IM_ASSERT(stb__dout == output + olen); + if (stb__dout != output + olen) return 0; + if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) + return 0; + return olen; + } else { + IM_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + IM_ASSERT(stb__dout <= output + olen); + if (stb__dout > output + olen) + return 0; + } +} + +//----------------------------------------------------------------------------- +// ProggyClean.ttf +// Copyright (c) 2004, 2005 Tristan Grimmer +// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) +// Download and more information at http://upperbounds.net +//----------------------------------------------------------------------------- +// File: 'ProggyClean.ttf' (41208 bytes) +// Exported using binary_to_compressed_c.cpp +//----------------------------------------------------------------------------- +static const char proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; + +static const char* GetDefaultCompressedFontDataTTFBase85() +{ + return proggy_clean_ttf_compressed_data_base85; +} + diff --git a/3rdparty/physfs/CMakeLists.txt b/3rdparty/physfs/CMakeLists.txt new file mode 100644 index 0000000..b2cc25a --- /dev/null +++ b/3rdparty/physfs/CMakeLists.txt @@ -0,0 +1,266 @@ +# PhysicsFS; a portable, flexible file i/o abstraction. +# Copyright (C) 2007 Ryan C. Gordon. +# +# This file is modified for internal use. +# +# TODO: make it use a more recent zlib +# +# Please see the file LICENSE.txt in the source's root directory. + +CMAKE_MINIMUM_REQUIRED(VERSION 2.4) + +PROJECT(PhysicsFS) +SET(PHYSFS_VERSION 2.0.3) + +# Increment this if/when we break backwards compatibility. +SET(PHYSFS_SOVERSION 1) + +# I hate that they define "WIN32" ... we're about to move to Win64...I hope! +IF(WIN32 AND NOT WINDOWS) + SET(WINDOWS TRUE) +ENDIF(WIN32 AND NOT WINDOWS) + +# Bleh, let's do it for "APPLE" too. +IF(APPLE AND NOT MACOSX) + SET(MACOSX TRUE) +ENDIF(APPLE AND NOT MACOSX) + +# For now, Haiku and BeOS are the same, as far as the build system cares. +IF(HAIKU AND NOT BEOS) + SET(BEOS TRUE) +ENDIF(HAIKU AND NOT BEOS) + +INCLUDE(CheckIncludeFile) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckCSourceCompiles) + +INCLUDE_DIRECTORIES(.) +#INCLUDE_DIRECTORIES(platform) +#INCLUDE_DIRECTORIES(archivers) + +IF(MACOSX) + # Fallback to older OS X on PowerPC to support wider range of systems... + IF(CMAKE_OSX_ARCHITECTURES MATCHES ppc) + ADD_DEFINITIONS(-DMAC_OS_X_VERSION_MIN_REQUIRED=1020) + SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -mmacosx-version-min=10.2") + ENDIF(CMAKE_OSX_ARCHITECTURES MATCHES ppc) + + # Need these everywhere... + ADD_DEFINITIONS(-fno-common) + SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -framework Carbon -framework IOKit") +ENDIF(MACOSX) + +# Add some gcc-specific command lines. +IF(CMAKE_COMPILER_IS_GNUCC) + # Always build with debug symbols...you can strip it later. + ADD_DEFINITIONS(-g -pipe -Werror -fsigned-char) + + # Stupid BeOS generates warnings in the system headers. + IF(NOT BEOS) + ADD_DEFINITIONS(-Wall) + ENDIF(NOT BEOS) + + CHECK_C_SOURCE_COMPILES(" + #if ((defined(__GNUC__)) && (__GNUC__ >= 4)) + int main(int argc, char **argv) { int is_gcc4 = 1; return 0; } + #else + #error This is not gcc4. + #endif + " PHYSFS_IS_GCC4) + + IF(PHYSFS_IS_GCC4) + # Not supported on several operating systems at this time. + IF(NOT OS2 AND NOT SOLARIS AND NOT WINDOWS) + ADD_DEFINITIONS(-fvisibility=hidden) + ENDIF(NOT OS2 AND NOT SOLARIS AND NOT WINDOWS) + ENDIF(PHYSFS_IS_GCC4) +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +IF(MSVC) + # VS.NET 8.0 got really really anal about strcpy, etc, which even if we + # cleaned up our code, zlib, etc still use...so disable the warning. + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS=1) +ENDIF(MSVC) + +# Basic chunks of source code ... + +SET(ZLIB_SRCS + zlib123/adler32.c + zlib123/compress.c + zlib123/crc32.c + zlib123/deflate.c + zlib123/gzio.c + zlib123/infback.c + zlib123/inffast.c + zlib123/inflate.c + zlib123/inftrees.c + zlib123/trees.c + zlib123/uncompr.c + zlib123/zutil.c +) + +SET(LZMA_SRCS + lzma/C/7zCrc.c + lzma/C/Archive/7z/7zBuffer.c + lzma/C/Archive/7z/7zDecode.c + lzma/C/Archive/7z/7zExtract.c + lzma/C/Archive/7z/7zHeader.c + lzma/C/Archive/7z/7zIn.c + lzma/C/Archive/7z/7zItem.c + lzma/C/Archive/7z/7zMethodID.c + lzma/C/Compress/Branch/BranchX86.c + lzma/C/Compress/Branch/BranchX86_2.c + lzma/C/Compress/Lzma/LzmaDecode.c +) + +IF(BEOS) + # We add this explicitly, since we don't want CMake to think this + # is a C++ project unless we're on BeOS. + SET(PHYSFS_BEOS_SRCS platform/beos.cpp) + FIND_LIBRARY(BE_LIBRARY be) + FIND_LIBRARY(ROOT_LIBRARY root) + SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${BE_LIBRARY} ${ROOT_LIBRARY}) +ENDIF(BEOS) + +# Almost everything is "compiled" here, but things that don't apply to the +# build are #ifdef'd out. This is to make it easy to embed PhysicsFS into +# another project or bring up a new build system: just compile all the source +# code and #define the things you want. +SET(PHYSFS_SRCS + physfs.c + physfs_byteorder.c + physfs_unicode.c + platform/os2.c + platform/pocketpc.c + platform/posix.c + platform/unix.c + platform/macosx.c + platform/windows.c + archivers/dir.c + archivers/grp.c + archivers/hog.c + archivers/lzma.c + archivers/mvl.c + archivers/qpak.c + archivers/wad.c + archivers/zip.c + ${PHYSFS_BEOS_SRCS} +) + +# platform layers ... + +IF(UNIX) + IF(BEOS) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) + SET(HAVE_PTHREAD_H TRUE) + ELSE(BEOS) + # !!! FIXME + # AC_DEFINE([PHYSFS_HAVE_LLSEEK], 1, [define if we have llseek]) + CHECK_INCLUDE_FILE(sys/ucred.h HAVE_UCRED_H) + IF(HAVE_UCRED_H) + ADD_DEFINITIONS(-DPHYSFS_HAVE_SYS_UCRED_H=1) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + ENDIF(HAVE_UCRED_H) + + CHECK_INCLUDE_FILE(mntent.h HAVE_MNTENT_H) + IF(HAVE_MNTENT_H) + ADD_DEFINITIONS(-DPHYSFS_HAVE_MNTENT_H=1) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + ENDIF(HAVE_MNTENT_H) + + CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H) + IF(HAVE_PTHREAD_H) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) + ENDIF(HAVE_PTHREAD_H) + + FIND_LIBRARY(PTHREAD_LIBRARY pthread) + IF(PTHREAD_LIBRARY) + SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${PTHREAD_LIBRARY}) + ENDIF(PTHREAD_LIBRARY) + ENDIF(BEOS) +ENDIF(UNIX) + +IF(WINDOWS) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) +ENDIF(WINDOWS) + +IF(NOT PHYSFS_HAVE_CDROM_SUPPORT) + ADD_DEFINITIONS(-DPHYSFS_NO_CDROM_SUPPORT=1) + MESSAGE(WARNING " ***") + MESSAGE(WARNING " *** There is no CD-ROM support in this build!") + MESSAGE(WARNING " *** PhysicsFS will just pretend there are no discs.") + MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,") + MESSAGE(WARNING " *** but is this what you REALLY wanted?") + MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)") + MESSAGE(WARNING " ***") +ENDIF(NOT PHYSFS_HAVE_CDROM_SUPPORT) + +IF(PHYSFS_HAVE_THREAD_SUPPORT) + ADD_DEFINITIONS(-D_REENTRANT -D_THREAD_SAFE) +ELSE(PHYSFS_HAVE_THREAD_SUPPORT) + ADD_DEFINITIONS(-DPHYSFS_NO_THREAD_SUPPORT=1) + MESSAGE(WARNING " ***") + MESSAGE(WARNING " *** There is no thread support in this build!") + MESSAGE(WARNING " *** PhysicsFS will NOT be reentrant!") + MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,") + MESSAGE(WARNING " *** but is this what you REALLY wanted?") + MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)") + MESSAGE(WARNING " ***") +ENDIF(PHYSFS_HAVE_THREAD_SUPPORT) + +CHECK_INCLUDE_FILE(assert.h HAVE_ASSERT_H) +IF(HAVE_ASSERT_H) + ADD_DEFINITIONS(-DHAVE_ASSERT_H=1) +ENDIF(HAVE_ASSERT_H) + +# Archivers ... + +OPTION(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE) +IF(PHYSFS_ARCHIVE_ZIP) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_ZIP=1) + SET(PHYSFS_NEED_ZLIB TRUE) +ENDIF(PHYSFS_ARCHIVE_ZIP) + +OPTION(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE) +IF(PHYSFS_ARCHIVE_7Z) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_7Z=1) + # !!! FIXME: rename to 7z.c? + SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${LZMA_SRCS}) +ENDIF(PHYSFS_ARCHIVE_7Z) + +OPTION(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE) +IF(PHYSFS_ARCHIVE_GRP) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_GRP=1) +ENDIF(PHYSFS_ARCHIVE_GRP) + +OPTION(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE) +IF(PHYSFS_ARCHIVE_WAD) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_WAD=1) +ENDIF(PHYSFS_ARCHIVE_WAD) + +OPTION(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE) +IF(PHYSFS_ARCHIVE_HOG) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_HOG=1) +ENDIF(PHYSFS_ARCHIVE_HOG) + +OPTION(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE) +IF(PHYSFS_ARCHIVE_MVL) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_MVL=1) +ENDIF(PHYSFS_ARCHIVE_MVL) + +OPTION(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE) +IF(PHYSFS_ARCHIVE_QPAK) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_QPAK=1) +ENDIF(PHYSFS_ARCHIVE_QPAK) + +INCLUDE_DIRECTORIES(zlib123) +ADD_DEFINITIONS(-DZ_PREFIX=1) +SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${ZLIB_SRCS}) + +ADD_LIBRARY(physfs STATIC ${PHYSFS_SRCS}) +SET_TARGET_PROPERTIES(physfs PROPERTIES OUTPUT_NAME "physfs") +SET(PHYSFS_LIB_TARGET physfs) + +# end of CMakeLists.txt ... diff --git a/3rdparty/physfs/CREDITS.txt b/3rdparty/physfs/CREDITS.txt new file mode 100644 index 0000000..91ab093 --- /dev/null +++ b/3rdparty/physfs/CREDITS.txt @@ -0,0 +1,119 @@ +Maintainer and general codemonkey: + Ryan C. Gordon + +Tons of win32 help: + Adam Gates + +More win32 hacking: + Gregory S. Read + +Fixes for missing current working directories, +PHYSFS_setSaneConfig() improvements, +other bugfixes: + David Hedbor + +Darwin support: + Patrick Stein + +configure fixes, +RPM specfile: + Edward Rudd + +GetLastModTime API, +other stuff: + John R. Hall + +Various support, fixes and suggestions: + Alexander Pipelka + +Russian translation, +Ruby bindings, +QPAK archiver: + Ed Sinjiashvili + +French translation: + Stéphane Peter + +Debian package support: + Colin Bayer + +"abs-file.h" in "extras" dir: + Adam D. Moss + +WinCE port and other Win32 patches: + Corona688 + +German translation: + Michael Renner + +Apple Project Builder support, +Mac OS X improvements: + Eric Wing + +iPhone support: + Christian Gmeiner + +HOG archiver, +MVL archiver: + Bradley Bell + +MIX archiver: + Sebastian Steinhauer + +Bug fixes: + Tolga Dalman + +Initial PHYSFS_mount() work: + Philip D. Bober + +Brazillian Portuguese translation: + Danny Angelo Carminati Grein + +Spanish translation: + Pedro J. Pérez + +MacOS Classic fixes, +MPW support, +bug fixes: + Chris Taylor + +Mingw support, +General bug fixes: + Matze Braun + +Haiku support: + scott mc + +Bug fixes: + Jörg Walter + +Bug fixes: + Olivier Boudeville + +Bug fixes: + Henk Boom + +Build system fixes: + Marc Kleine-Budde + +Windows .rc file, +7zip/lzma archiver: + Dennis Schridde + +OS/2 updates: + Dave Yeo + +Bug fixes: + Patrice Mandin + +Bug fixes: + Lauri Kasanen + +Bug fixes: + Andreas Karlsson + +Other stuff: + Your name here! Patches go to icculus@icculus.org ... + +/* end of CREDITS.txt ... */ + diff --git a/3rdparty/physfs/LICENSE.txt b/3rdparty/physfs/LICENSE.txt new file mode 100644 index 0000000..702b53b --- /dev/null +++ b/3rdparty/physfs/LICENSE.txt @@ -0,0 +1,44 @@ + + Copyright (c) 2001-2016 Ryan C. Gordon and others. + + 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. + + Ryan C. Gordon + + + + +Notes, separate from the license. This is not legal advice. + +Versions of PhysicsFS prior to 0.1.9 are licensed under the GNU Lesser General + Public License, which restricts you significantly more. For your own safety, + please make sure you've got 0.1.9 or later if you plan to use physfs in a + commercial or closed-source project. + +Optional pieces of PhysicsFS may fall under other licenses, please consult +your lawyer for legal advice, which this is not... + + zlib: if you enable ZIP archive support, PhysicsFS uses zlib. Its license + requirements are identical to PhysicsFS. + Please see zlib123/README for details. + + lzma: if you enable LZMA (7zip) support, PhysicsFS uses the lzma sdk. + It uses the LGPL license, with exceptions for closed-source programs. + Please see lzma/lzma.txt for details. + diff --git a/3rdparty/physfs/archivers/dir.c b/3rdparty/physfs/archivers/dir.c new file mode 100644 index 0000000..a580743 --- /dev/null +++ b/3rdparty/physfs/archivers/dir.c @@ -0,0 +1,283 @@ +/* + * Standard directory I/O support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval; + retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount); + return(retval); +} /* DIR_read */ + + +static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval; + retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount); + return(retval); +} /* DIR_write */ + + +static int DIR_eof(fvoid *opaque) +{ + return(__PHYSFS_platformEOF(opaque)); +} /* DIR_eof */ + + +static PHYSFS_sint64 DIR_tell(fvoid *opaque) +{ + return(__PHYSFS_platformTell(opaque)); +} /* DIR_tell */ + + +static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + return(__PHYSFS_platformSeek(opaque, offset)); +} /* DIR_seek */ + + +static PHYSFS_sint64 DIR_fileLength(fvoid *opaque) +{ + return(__PHYSFS_platformFileLength(opaque)); +} /* DIR_fileLength */ + + +static int DIR_fileClose(fvoid *opaque) +{ + /* + * we manually flush the buffer, since that's the place a close will + * most likely fail, but that will leave the file handle in an undefined + * state if it fails. Flush failures we can recover from. + */ + BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0); + BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0); + return(1); +} /* DIR_fileClose */ + + +static int DIR_isArchive(const char *filename, int forWriting) +{ + /* directories ARE archives in this driver... */ + return(__PHYSFS_platformIsDirectory(filename)); +} /* DIR_isArchive */ + + +static void *DIR_openArchive(const char *name, int forWriting) +{ + const char *dirsep = PHYSFS_getDirSeparator(); + char *retval = NULL; + size_t namelen = strlen(name); + size_t seplen = strlen(dirsep); + + /* !!! FIXME: when is this not called right before openArchive? */ + BAIL_IF_MACRO(!DIR_isArchive(name, forWriting), + ERR_UNSUPPORTED_ARCHIVE, 0); + + retval = allocator.Malloc(namelen + seplen + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* make sure there's a dir separator at the end of the string */ + strcpy(retval, name); + if (strcmp((name + namelen) - seplen, dirsep) != 0) + strcat(retval, dirsep); + + return(retval); +} /* DIR_openArchive */ + + +static void DIR_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL); + if (d != NULL) + { + __PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb, + origdir, callbackdata); + allocator.Free(d); + } /* if */ +} /* DIR_enumerateFiles */ + + +static int DIR_exists(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformExists(f); + allocator.Free(f); + return(retval); +} /* DIR_exists */ + + +static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval = 0; + + BAIL_IF_MACRO(d == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(d); + if (*fileExists) + retval = __PHYSFS_platformIsDirectory(d); + allocator.Free(d); + return(retval); +} /* DIR_isDirectory */ + + +static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval = 0; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(f); + if (*fileExists) + retval = __PHYSFS_platformIsSymLink(f); + allocator.Free(f); + return(retval); +} /* DIR_isSymLink */ + + +static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + PHYSFS_sint64 retval = -1; + + BAIL_IF_MACRO(d == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(d); + if (*fileExists) + retval = __PHYSFS_platformGetLastModTime(d); + allocator.Free(d); + return(retval); +} /* DIR_getLastModTime */ + + +static fvoid *doOpen(dvoid *opaque, const char *name, + void *(*openFunc)(const char *filename), + int *fileExists) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + void *rc = NULL; + + BAIL_IF_MACRO(f == NULL, NULL, NULL); + + if (fileExists != NULL) + { + *fileExists = __PHYSFS_platformExists(f); + if (!(*fileExists)) + { + allocator.Free(f); + return(NULL); + } /* if */ + } /* if */ + + rc = openFunc(f); + allocator.Free(f); + + return((fvoid *) rc); +} /* doOpen */ + + +static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist) +{ + return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist)); +} /* DIR_openRead */ + + +static fvoid *DIR_openWrite(dvoid *opaque, const char *filename) +{ + return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL)); +} /* DIR_openWrite */ + + +static fvoid *DIR_openAppend(dvoid *opaque, const char *filename) +{ + return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL)); +} /* DIR_openAppend */ + + +static int DIR_remove(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformDelete(f); + allocator.Free(f); + return(retval); +} /* DIR_remove */ + + +static int DIR_mkdir(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformMkDir(f); + allocator.Free(f); + return(retval); +} /* DIR_mkdir */ + + +static void DIR_dirClose(dvoid *opaque) +{ + allocator.Free(opaque); +} /* DIR_dirClose */ + + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR = +{ + "", + DIR_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + + +const PHYSFS_Archiver __PHYSFS_Archiver_DIR = +{ + &__PHYSFS_ArchiveInfo_DIR, + DIR_isArchive, /* isArchive() method */ + DIR_openArchive, /* openArchive() method */ + DIR_enumerateFiles, /* enumerateFiles() method */ + DIR_exists, /* exists() method */ + DIR_isDirectory, /* isDirectory() method */ + DIR_isSymLink, /* isSymLink() method */ + DIR_getLastModTime, /* getLastModTime() method */ + DIR_openRead, /* openRead() method */ + DIR_openWrite, /* openWrite() method */ + DIR_openAppend, /* openAppend() method */ + DIR_remove, /* remove() method */ + DIR_mkdir, /* mkdir() method */ + DIR_dirClose, /* dirClose() method */ + DIR_read, /* read() method */ + DIR_write, /* write() method */ + DIR_eof, /* eof() method */ + DIR_tell, /* tell() method */ + DIR_seek, /* seek() method */ + DIR_fileLength, /* fileLength() method */ + DIR_fileClose /* fileClose() method */ +}; + +/* end of dir.c ... */ + diff --git a/3rdparty/physfs/archivers/grp.c b/3rdparty/physfs/archivers/grp.c new file mode 100644 index 0000000..8c63f1e --- /dev/null +++ b/3rdparty/physfs/archivers/grp.c @@ -0,0 +1,475 @@ +/* + * GRP support routines for PhysicsFS. + * + * This driver handles BUILD engine archives ("groupfiles"). This format + * (but not this driver) was put together by Ken Silverman. + * + * The format is simple enough. In Ken's words: + * + * What's the .GRP file format? + * + * The ".grp" file format is just a collection of a lot of files stored + * into 1 big one. I tried to make the format as simple as possible: The + * first 12 bytes contains my name, "KenSilverman". The next 4 bytes is + * the number of files that were compacted into the group file. Then for + * each file, there is a 16 byte structure, where the first 12 bytes are + * the filename, and the last 4 bytes are the file's size. The rest of + * the group file is just the raw data packed one after the other in the + * same order as the list of files. + * + * (That info is from http://www.advsys.net/ken/build.htm ...) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#if (defined PHYSFS_SUPPORTS_GRP) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} GRPentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + GRPentry *entries; +} GRPinfo; + +typedef struct +{ + void *handle; + GRPentry *entry; + PHYSFS_uint32 curPos; +} GRPfileinfo; + + +static void GRP_dirClose(dvoid *opaque) +{ + GRPinfo *info = ((GRPinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* GRP_dirClose */ + + +static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* GRP_read */ + + +static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* GRP_write */ + + +static int GRP_eof(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* GRP_eof */ + + +static PHYSFS_sint64 GRP_tell(fvoid *opaque) +{ + return(((GRPfileinfo *) opaque)->curPos); +} /* GRP_tell */ + + +static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* GRP_seek */ + + +static PHYSFS_sint64 GRP_fileLength(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* GRP_fileLength */ + + +static int GRP_fileClose(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* GRP_fileClose */ + + +static int grp_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[12]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1) + goto openGrp_failed; + + if (memcmp(buf, "KenSilverman", 12) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openGrp_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openGrp_failed; + + *count = PHYSFS_swapULE32(*count); + + return(1); + +openGrp_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* grp_open */ + + +static int GRP_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = grp_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* GRP_isArchive */ + + +static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const GRPentry *a = (const GRPentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* grp_entry_cmp */ + + +static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + GRPentry tmp; + GRPentry *first = &(((GRPentry *) _a)[one]); + GRPentry *second = &(((GRPentry *) _a)[two]); + memcpy(&tmp, first, sizeof (GRPentry)); + memcpy(first, second, sizeof (GRPentry)); + memcpy(second, &tmp, sizeof (GRPentry)); + } /* if */ +} /* grp_entry_swap */ + + +static int grp_load_entries(const char *name, int forWriting, GRPinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 location = 16; /* sizeof sig. */ + GRPentry *entry; + char *ptr; + + BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + location += (16 * fileCount); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->name[12] = '\0'; /* name isn't null-terminated in file. */ + if ((ptr = strchr(entry->name, ' ')) != NULL) + *ptr = '\0'; /* trim extra spaces. */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = location; + location += entry->size; + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + grp_entry_cmp, grp_entry_swap); + return(1); +} /* grp_load_entries */ + + +static void *GRP_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + + memset(info, '\0', sizeof (GRPinfo)); + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed); + + if (!grp_load_entries(name, forWriting, info)) + goto GRP_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + + return(info); + +GRP_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* GRP_openArchive */ + + +static void GRP_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in GRP files. */ + if (*dname == '\0') + { + GRPinfo *info = (GRPinfo *) opaque; + GRPentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* GRP_enumerateFiles */ + + +static GRPentry *grp_find_entry(GRPinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + GRPentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = strcmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* grp_find_entry */ + + +static int GRP_exists(dvoid *opaque, const char *name) +{ + return(grp_find_entry((GRPinfo *) opaque, name) != NULL); +} /* GRP_exists */ + + +static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = GRP_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* GRP_isDirectory */ + + +static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = GRP_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* GRP_isSymLink */ + + +static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + GRPinfo *info = (GRPinfo *) opaque; + PHYSFS_sint64 retval = -1; + + *fileExists = (grp_find_entry(info, name) != NULL); + if (*fileExists) /* use time of GRP itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* GRP_getLastModTime */ + + +static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + GRPinfo *info = (GRPinfo *) opaque; + GRPfileinfo *finfo; + GRPentry *entry; + + entry = grp_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* GRP_openRead */ + + +static fvoid *GRP_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* GRP_openWrite */ + + +static fvoid *GRP_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* GRP_openAppend */ + + +static int GRP_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* GRP_remove */ + + +static int GRP_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* GRP_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP = +{ + "GRP", + GRP_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_GRP = +{ + &__PHYSFS_ArchiveInfo_GRP, + GRP_isArchive, /* isArchive() method */ + GRP_openArchive, /* openArchive() method */ + GRP_enumerateFiles, /* enumerateFiles() method */ + GRP_exists, /* exists() method */ + GRP_isDirectory, /* isDirectory() method */ + GRP_isSymLink, /* isSymLink() method */ + GRP_getLastModTime, /* getLastModTime() method */ + GRP_openRead, /* openRead() method */ + GRP_openWrite, /* openWrite() method */ + GRP_openAppend, /* openAppend() method */ + GRP_remove, /* remove() method */ + GRP_mkdir, /* mkdir() method */ + GRP_dirClose, /* dirClose() method */ + GRP_read, /* read() method */ + GRP_write, /* write() method */ + GRP_eof, /* eof() method */ + GRP_tell, /* tell() method */ + GRP_seek, /* seek() method */ + GRP_fileLength, /* fileLength() method */ + GRP_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_GRP */ + +/* end of grp.c ... */ + diff --git a/3rdparty/physfs/archivers/hog.c b/3rdparty/physfs/archivers/hog.c new file mode 100644 index 0000000..43620f6 --- /dev/null +++ b/3rdparty/physfs/archivers/hog.c @@ -0,0 +1,514 @@ +/* + * HOG support routines for PhysicsFS. + * + * This driver handles Descent I/II HOG archives. + * + * The format is very simple: + * + * The file always starts with the 3-byte signature "DHF" (Descent + * HOG file). After that the files of a HOG are just attached after + * another, divided by a 17 bytes header, which specifies the name + * and length (in bytes) of the forthcoming file! So you just read + * the header with its information of how big the following file is, + * and then skip exact that number of bytes to get to the next file + * in that HOG. + * + * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * char data[file_size]; // The file data + * } FILE_STRUCT; // Repeated until the end of the file. + * + * (That info is from http://www.descent2.com/ddn/specs/hog/) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#if (defined PHYSFS_SUPPORTS_HOG) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* + * One HOGentry is kept for each file in an open HOG archive. + */ +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} HOGentry; + +/* + * One HOGinfo is kept for each open HOG archive. + */ +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + HOGentry *entries; +} HOGinfo; + +/* + * One HOGfileinfo is kept for each open file in a HOG archive. + */ +typedef struct +{ + void *handle; + HOGentry *entry; + PHYSFS_uint32 curPos; +} HOGfileinfo; + + +static void HOG_dirClose(dvoid *opaque) +{ + HOGinfo *info = ((HOGinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* HOG_dirClose */ + + +static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* HOG_read */ + + +static PHYSFS_sint64 HOG_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* HOG_write */ + + +static int HOG_eof(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* HOG_eof */ + + +static PHYSFS_sint64 HOG_tell(fvoid *opaque) +{ + return(((HOGfileinfo *) opaque)->curPos); +} /* HOG_tell */ + + +static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* HOG_seek */ + + +static PHYSFS_sint64 HOG_fileLength(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* HOG_fileLength */ + + +static int HOG_fileClose(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* HOG_fileClose */ + + +static int hog_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[13]; + PHYSFS_uint32 size; + PHYSFS_sint64 pos; + + *count = 0; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1) + goto openHog_failed; + + if (memcmp(buf, "DHF", 3) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openHog_failed; + } /* if */ + + while (1) + { + if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1) + break; /* eof here is ok */ + + if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1) + goto openHog_failed; + + size = PHYSFS_swapULE32(size); + + (*count)++; + + /* Skip over entry... */ + pos = __PHYSFS_platformTell(*fh); + if (pos == -1) + goto openHog_failed; + if (!__PHYSFS_platformSeek(*fh, pos + size)) + goto openHog_failed; + } /* while */ + + /* Rewind to start of entries... */ + if (!__PHYSFS_platformSeek(*fh, 3)) + goto openHog_failed; + + return(1); + +openHog_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* hog_open */ + + +static int HOG_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = hog_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* HOG_isArchive */ + + +static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const HOGentry *a = (const HOGentry *) _a; + return(__PHYSFS_stricmpASCII(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* hog_entry_cmp */ + + +static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + HOGentry tmp; + HOGentry *first = &(((HOGentry *) _a)[one]); + HOGentry *second = &(((HOGentry *) _a)[two]); + memcpy(&tmp, first, sizeof (HOGentry)); + memcpy(first, second, sizeof (HOGentry)); + memcpy(second, &tmp, sizeof (HOGentry)); + } /* if */ +} /* hog_entry_swap */ + + +static int hog_load_entries(const char *name, int forWriting, HOGinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + HOGentry *entry; + + BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = (unsigned int) __PHYSFS_platformTell(fh); + if (entry->startPos == -1) + { + __PHYSFS_platformClose(fh); + return(0); + } + + /* Skip over entry */ + if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size)) + { + __PHYSFS_platformClose(fh); + return(0); + } + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + hog_entry_cmp, hog_entry_swap); + return(1); +} /* hog_load_entries */ + + +static void *HOG_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + memset(info, '\0', sizeof (HOGinfo)); + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed); + + if (!hog_load_entries(name, forWriting, info)) + goto HOG_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + + return(info); + +HOG_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* HOG_openArchive */ + + +static void HOG_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in HOG files. */ + if (*dname == '\0') + { + HOGinfo *info = (HOGinfo *) opaque; + HOGentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* HOG_enumerateFiles */ + + +static HOGentry *hog_find_entry(HOGinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + HOGentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_stricmpASCII(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* hog_find_entry */ + + +static int HOG_exists(dvoid *opaque, const char *name) +{ + return(hog_find_entry(((HOGinfo *) opaque), name) != NULL); +} /* HOG_exists */ + + +static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* HOG_isDirectory */ + + +static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* HOG_isSymLink */ + + +static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (hog_find_entry(info, name) != NULL); + if (*fileExists) /* use time of HOG itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* HOG_getLastModTime */ + + +static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) opaque); + HOGfileinfo *finfo; + HOGentry *entry; + + entry = hog_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* HOG_openRead */ + + +static fvoid *HOG_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openWrite */ + + +static fvoid *HOG_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openAppend */ + + +static int HOG_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_remove */ + + +static int HOG_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG = +{ + "HOG", + HOG_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_HOG = +{ + &__PHYSFS_ArchiveInfo_HOG, + HOG_isArchive, /* isArchive() method */ + HOG_openArchive, /* openArchive() method */ + HOG_enumerateFiles, /* enumerateFiles() method */ + HOG_exists, /* exists() method */ + HOG_isDirectory, /* isDirectory() method */ + HOG_isSymLink, /* isSymLink() method */ + HOG_getLastModTime, /* getLastModTime() method */ + HOG_openRead, /* openRead() method */ + HOG_openWrite, /* openWrite() method */ + HOG_openAppend, /* openAppend() method */ + HOG_remove, /* remove() method */ + HOG_mkdir, /* mkdir() method */ + HOG_dirClose, /* dirClose() method */ + HOG_read, /* read() method */ + HOG_write, /* write() method */ + HOG_eof, /* eof() method */ + HOG_tell, /* tell() method */ + HOG_seek, /* seek() method */ + HOG_fileLength, /* fileLength() method */ + HOG_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_HOG */ + +/* end of hog.c ... */ + diff --git a/3rdparty/physfs/archivers/lzma.c b/3rdparty/physfs/archivers/lzma.c new file mode 100644 index 0000000..dfdefc1 --- /dev/null +++ b/3rdparty/physfs/archivers/lzma.c @@ -0,0 +1,736 @@ +/* + * LZMA support routines for PhysicsFS. + * + * Please see the file lzma.txt in the lzma/ directory. + * + * This file was written by Dennis Schridde, with some peeking at "7zMain.c" + * by Igor Pavlov. + */ + +#if (defined PHYSFS_SUPPORTS_7Z) + +#include +#include +#include + +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#include "lzma/C/7zCrc.h" +#include "lzma/C/Archive/7z/7zIn.h" +#include "lzma/C/Archive/7z/7zExtract.h" + + +/* 7z internal from 7zIn.c */ +extern int TestSignatureCandidate(Byte *testBytes); + + +#ifdef _LZMA_IN_CB +# define BUFFER_SIZE (1 << 12) +#endif /* _LZMA_IN_CB */ + + +/* + * Carries filestream metadata through 7z + */ +typedef struct _FileInputStream +{ + ISzAlloc allocImp; /* Allocation implementation, used by 7z */ + ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */ + ISzInStream inStream; /* Input stream with read callbacks, used by 7z */ + void *file; /* Filehandle, used by read implementation */ +#ifdef _LZMA_IN_CB + Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */ +#endif /* _LZMA_IN_CB */ +} FileInputStream; + +/* + * In the 7z format archives are splited into blocks, those are called folders + * Set by LZMA_read() +*/ +typedef struct _LZMAfolder +{ + PHYSFS_uint32 index; /* Index of folder in archive */ + PHYSFS_uint32 references; /* Number of files using this block */ + PHYSFS_uint8 *cache; /* Cached folder */ + size_t size; /* Size of folder */ +} LZMAfolder; + +/* + * Set by LZMA_openArchive(), except folder which gets it's values + * in LZMA_read() + */ +typedef struct _LZMAarchive +{ + struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */ + LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */ + CArchiveDatabaseEx db; /* For 7z: Database */ + FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */ +} LZMAarchive; + +/* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */ +typedef struct _LZMAfile +{ + PHYSFS_uint32 index; /* Index of file in archive */ + LZMAarchive *archive; /* Link to corresponding archive */ + LZMAfolder *folder; /* Link to corresponding folder */ + CFileItem *item; /* For 7z: File info, eg. name, size */ + size_t offset; /* Offset in folder */ + size_t position; /* Current "virtual" position in file */ +} LZMAfile; + + +/* Memory management implementations to be passed to 7z */ + +static void *SzAllocPhysicsFS(size_t size) +{ + return ((size == 0) ? NULL : allocator.Malloc(size)); +} /* SzAllocPhysicsFS */ + + +static void SzFreePhysicsFS(void *address) +{ + if (address != NULL) + allocator.Free(address); +} /* SzFreePhysicsFS */ + + +/* Filesystem implementations to be passed to 7z */ + +#ifdef _LZMA_IN_CB + +/* + * Read implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize, + size_t *processedSize) +{ + FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */ + PHYSFS_sint64 processedSizeLoc = 0; + + if (maxReqSize > BUFFER_SIZE) + maxReqSize = BUFFER_SIZE; + processedSizeLoc = __PHYSFS_platformRead(s->file, s->buffer, 1, maxReqSize); + *buffer = s->buffer; + if (processedSize != NULL) + *processedSize = (size_t) processedSizeLoc; + + return SZ_OK; +} /* SzFileReadImp */ + +#else + +/* + * Read implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size, + size_t *processedSize) +{ + FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */ + size_t processedSizeLoc = __PHYSFS_platformRead(s->file, buffer, 1, size); + if (processedSize != 0) + *processedSize = processedSizeLoc; + return SZ_OK; +} /* SzFileReadImp */ + +#endif + +/* + * Seek implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) +{ + FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */ + if (__PHYSFS_platformSeek(s->file, (PHYSFS_uint64) pos)) + return SZ_OK; + return SZE_FAIL; +} /* SzFileSeekImp */ + + +/* + * Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp + */ +static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft) +{ + /* MS counts in nanoseconds ... */ + const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000); + /* MS likes to count seconds since 01.01.1601 ... */ + const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600); + + PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32); + return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF; +} /* lzma_filetime_to_unix_timestamp */ + + +/* + * Compare a file with a given name, C89 stdlib variant + * Used for sorting + */ +static int lzma_file_cmp_stdlib(const void *key, const void *object) +{ + const char *name = (const char *) key; + LZMAfile *file = (LZMAfile *) object; + return(strcmp(name, file->item->Name)); +} /* lzma_file_cmp_posix */ + + +/* + * Compare two files with each other based on the name + * Used for sorting + */ +static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + LZMAfile *files = (LZMAfile *) _a; + return(strcmp(files[one].item->Name, files[two].item->Name)); +} /* lzma_file_cmp */ + + +/* + * Swap two entries in the file array + */ +static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + LZMAfile tmp; + LZMAfile *first = &(((LZMAfile *) _a)[one]); + LZMAfile *second = &(((LZMAfile *) _a)[two]); + memcpy(&tmp, first, sizeof (LZMAfile)); + memcpy(first, second, sizeof (LZMAfile)); + memcpy(second, &tmp, sizeof (LZMAfile)); +} /* lzma_file_swap */ + + +/* + * Find entry 'name' in 'archive' + */ +static LZMAfile * lzma_find_file(LZMAarchive *archive, const char *name) +{ + LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */ + + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL); + + return(file); +} /* lzma_find_file */ + + +/* + * Load metadata for the file at given index + */ +static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex) +{ + LZMAfile *file = &archive->files[fileIndex]; + PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex]; + + file->index = fileIndex; /* Store index into 7z array, since we sort our own. */ + file->archive = archive; + file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */ + file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */ + file->position = 0; + file->offset = 0; /* Offset will be set by LZMA_read() */ + + return(1); +} /* lzma_load_file */ + + +/* + * Load metadata for all files + */ +static int lzma_files_init(LZMAarchive *archive) +{ + PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles; + + for (fileIndex = 0; fileIndex < numFiles; fileIndex++ ) + { + if (!lzma_file_init(archive, fileIndex)) + { + return(0); /* FALSE on failure */ + } + } /* for */ + + __PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap); + + return(1); +} /* lzma_load_files */ + + +/* + * Initialise specified archive + */ +static void lzma_archive_init(LZMAarchive *archive) +{ + memset(archive, 0, sizeof(*archive)); + + /* Prepare callbacks for 7z */ + archive->stream.inStream.Read = SzFileReadImp; + archive->stream.inStream.Seek = SzFileSeekImp; + + archive->stream.allocImp.Alloc = SzAllocPhysicsFS; + archive->stream.allocImp.Free = SzFreePhysicsFS; + + archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS; + archive->stream.allocTempImp.Free = SzFreePhysicsFS; +} + + +/* + * Deinitialise archive + */ +static void lzma_archive_exit(LZMAarchive *archive) +{ + /* Free arrays */ + allocator.Free(archive->folders); + allocator.Free(archive->files); + allocator.Free(archive); +} + +/* + * Wrap all 7z calls in this, so the physfs error state is set appropriately. + */ +static int lzma_err(SZ_RESULT rc) +{ + switch (rc) + { + case SZ_OK: /* Same as LZMA_RESULT_OK */ + break; + case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */ + __PHYSFS_setError(ERR_DATA_ERROR); + break; + case SZE_OUTOFMEMORY: + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + break; + case SZE_CRC_ERROR: + __PHYSFS_setError(ERR_CORRUPTED); + break; + case SZE_NOTIMPL: + __PHYSFS_setError(ERR_NOT_IMPLEMENTED); + break; + case SZE_FAIL: + __PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */ + break; + case SZE_ARCHIVE_ERROR: + __PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */ + break; + default: + __PHYSFS_setError(ERR_UNKNOWN_ERROR); + } /* switch */ + + return(rc); +} /* lzma_err */ + + +static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + LZMAfile *file = (LZMAfile *) opaque; + + size_t wantedSize = objSize*objCount; + size_t remainingSize = file->item->Size - file->position; + size_t fileSize = 0; + + BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */ + BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0); + + if (remainingSize < wantedSize) + { + wantedSize = remainingSize - (remainingSize % objSize); + objCount = (PHYSFS_uint32) (remainingSize / objSize); + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ + } /* if */ + + /* Only decompress the folder if it is not allready cached */ + if (file->folder->cache == NULL) + { + int rc = lzma_err(SzExtract( + &file->archive->stream.inStream, /* compressed data */ + &file->archive->db, /* 7z's database, containing everything */ + file->index, /* Index into database arrays */ + /* Index of cached folder, will be changed by SzExtract */ + &file->folder->index, + /* Cache for decompressed folder, allocated/freed by SzExtract */ + &file->folder->cache, + /* Size of cache, will be changed by SzExtract */ + &file->folder->size, + /* Offset of this file inside the cache, set by SzExtract */ + &file->offset, + &fileSize, /* Size of this file */ + &file->archive->stream.allocImp, + &file->archive->stream.allocTempImp)); + + if (rc != SZ_OK) + return -1; + } /* if */ + + /* Copy wanted bytes over from cache to outBuffer */ + memcpy(outBuffer, + (file->folder->cache + + file->offset + file->position), + wantedSize); + file->position += wantedSize; /* Increase virtual position */ + + return objCount; +} /* LZMA_read */ + + +static PHYSFS_sint64 LZMA_write(fvoid *opaque, const void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* LZMA_write */ + + +static int LZMA_eof(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->position >= file->item->Size); +} /* LZMA_eof */ + + +static PHYSFS_sint64 LZMA_tell(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->position); +} /* LZMA_tell */ + + +static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + LZMAfile *file = (LZMAfile *) opaque; + + BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0); + BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0); + + file->position = offset; /* We only use a virtual position... */ + + return 1; +} /* LZMA_seek */ + + +static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->item->Size); +} /* LZMA_fileLength */ + + +static int LZMA_fileClose(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + + BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, 0); + + /* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */ + if (file->folder->references > 0) + file->folder->references--; + if (file->folder->references == 0) + { + /* Free the cache which might have been allocated by LZMA_read() */ + allocator.Free(file->folder->cache); + file->folder->cache = NULL; + } + + return(1); +} /* LZMA_fileClose */ + + +static int LZMA_isArchive(const char *filename, int forWriting) +{ + PHYSFS_uint8 sig[k7zSignatureSize]; + void *in; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + in = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(in == NULL, NULL, 0); + + /* Read signature bytes */ + if (__PHYSFS_platformRead(in, sig, k7zSignatureSize, 1) != 1) + { + __PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */ + BAIL_MACRO(NULL, 0); + } + + __PHYSFS_platformClose(in); + + /* Test whether sig is the 7z signature */ + return(TestSignatureCandidate(sig)); +} /* LZMA_isArchive */ + + +static void *LZMA_openArchive(const char *name, int forWriting) +{ + size_t len = 0; + LZMAarchive *archive = NULL; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + BAIL_IF_MACRO(!LZMA_isArchive(name,forWriting), ERR_UNSUPPORTED_ARCHIVE, 0); + + archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive)); + BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL); + + lzma_archive_init(archive); + + if ( (archive->stream.file = __PHYSFS_platformOpenRead(name)) == NULL ) + { + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + return(NULL); /* Error is set by platformOpenRead! */ + } + + CrcGenerateTable(); + SzArDbExInit(&archive->db); + if (lzma_err(SzArchiveOpen(&archive->stream.inStream, + &archive->db, + &archive->stream.allocImp, + &archive->stream.allocTempImp)) != SZ_OK) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + return NULL; /* Error is set by lzma_err! */ + } /* if */ + + len = archive->db.Database.NumFiles * sizeof (LZMAfile); + archive->files = (LZMAfile *) allocator.Malloc(len); + if (archive->files == NULL) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } + + /* + * Init with 0 so we know when a folder is already cached + * Values will be set by LZMA_openRead() + */ + memset(archive->files, 0, len); + + len = archive->db.Database.NumFolders * sizeof (LZMAfolder); + archive->folders = (LZMAfolder *) allocator.Malloc(len); + if (archive->folders == NULL) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } + + /* + * Init with 0 so we know when a folder is already cached + * Values will be set by LZMA_read() + */ + memset(archive->folders, 0, len); + + if(!lzma_files_init(archive)) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL); + } + + return(archive); +} /* LZMA_openArchive */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, size_t flen) +{ + char *newstr = __PHYSFS_smallAlloc(flen + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, flen); + newstr[flen] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void LZMA_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + size_t dlen = strlen(dname), + dlen_inc = dlen + ((dlen > 0) ? 1 : 0); + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = NULL, + *lastFile = &archive->files[archive->db.Database.NumFiles]; + if (dlen) + { + file = lzma_find_file(archive, dname); + if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */ + file += 1; + } + else + { + file = archive->files; + } + + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, ); + + while (file < lastFile) + { + const char * fname = file->item->Name; + const char * dirNameEnd = fname + dlen_inc; + + if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */ + break; + + if (strchr(dirNameEnd, '/')) /* Skip subdirs */ + { + file++; + continue; + } + + /* Do the actual callback... */ + doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd)); + + file++; + } +} /* LZMA_enumerateFiles */ + + +static int LZMA_exists(dvoid *opaque, const char *name) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + return(lzma_find_file(archive, name) != NULL); +} /* LZMA_exists */ + + +static PHYSFS_sint64 LZMA_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + + BAIL_IF_MACRO(file == NULL, NULL, -1); + BAIL_IF_MACRO(!file->item->IsLastWriteTimeDefined, NULL, -1); /* write-time may not be defined for every file */ + + return(lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime)); +} /* LZMA_getLastModTime */ + + +static int LZMA_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + + return(file == NULL ? 0 : file->item->IsDirectory); +} /* LZMA_isDirectory */ + + +static int LZMA_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_isSymLink */ + + +static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, NULL); + + file->position = 0; + file->folder->references++; /* Increase refcount for automatic cleanup... */ + + return(file); +} /* LZMA_openRead */ + + +static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* LZMA_openWrite */ + + +static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* LZMA_openAppend */ + + +static void LZMA_dirClose(dvoid *opaque) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles; + + for (fileIndex = 0; fileIndex < numFiles; fileIndex++) + { + LZMA_fileClose(&archive->files[fileIndex]); + } /* for */ + + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); +} /* LZMA_dirClose */ + + +static int LZMA_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_remove */ + + +static int LZMA_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA = +{ + "7Z", + LZMA_ARCHIVE_DESCRIPTION, + "Dennis Schridde ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_LZMA = +{ + &__PHYSFS_ArchiveInfo_LZMA, + LZMA_isArchive, /* isArchive() method */ + LZMA_openArchive, /* openArchive() method */ + LZMA_enumerateFiles, /* enumerateFiles() method */ + LZMA_exists, /* exists() method */ + LZMA_isDirectory, /* isDirectory() method */ + LZMA_isSymLink, /* isSymLink() method */ + LZMA_getLastModTime, /* getLastModTime() method */ + LZMA_openRead, /* openRead() method */ + LZMA_openWrite, /* openWrite() method */ + LZMA_openAppend, /* openAppend() method */ + LZMA_remove, /* remove() method */ + LZMA_mkdir, /* mkdir() method */ + LZMA_dirClose, /* dirClose() method */ + LZMA_read, /* read() method */ + LZMA_write, /* write() method */ + LZMA_eof, /* eof() method */ + LZMA_tell, /* tell() method */ + LZMA_seek, /* seek() method */ + LZMA_fileLength, /* fileLength() method */ + LZMA_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_7Z */ + +/* end of lzma.c ... */ + diff --git a/3rdparty/physfs/archivers/mvl.c b/3rdparty/physfs/archivers/mvl.c new file mode 100644 index 0000000..8fbda23 --- /dev/null +++ b/3rdparty/physfs/archivers/mvl.c @@ -0,0 +1,471 @@ +/* + * MVL support routines for PhysicsFS. + * + * This driver handles Descent II Movielib archives. + * + * The file format of MVL is quite easy... + * + * //MVL File format - Written by Heiko Herrmann + * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library + * + * int num_files; // the number of files in this MVL + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * }DIR_STRUCT[num_files]; + * + * struct { + * char data[file_size]; // The file data + * }FILE_STRUCT[num_files]; + * + * (That info is from http://www.descent2.com/ddn/specs/mvl/) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#if (defined PHYSFS_SUPPORTS_MVL) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} MVLentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + MVLentry *entries; +} MVLinfo; + +typedef struct +{ + void *handle; + MVLentry *entry; + PHYSFS_uint32 curPos; +} MVLfileinfo; + + +static void MVL_dirClose(dvoid *opaque) +{ + MVLinfo *info = ((MVLinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* MVL_dirClose */ + + +static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* MVL_read */ + + +static PHYSFS_sint64 MVL_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* MVL_write */ + + +static int MVL_eof(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* MVL_eof */ + + +static PHYSFS_sint64 MVL_tell(fvoid *opaque) +{ + return(((MVLfileinfo *) opaque)->curPos); +} /* MVL_tell */ + + +static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* MVL_seek */ + + +static PHYSFS_sint64 MVL_fileLength(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* MVL_fileLength */ + + +static int MVL_fileClose(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* MVL_fileClose */ + + +static int mvl_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[4]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1) + goto openMvl_failed; + + if (memcmp(buf, "DMVL", 4) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openMvl_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openMvl_failed; + + *count = PHYSFS_swapULE32(*count); + + return(1); + +openMvl_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* mvl_open */ + + +static int MVL_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = mvl_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* MVL_isArchive */ + + +static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const MVLentry *a = (const MVLentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* mvl_entry_cmp */ + + +static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + MVLentry tmp; + MVLentry *first = &(((MVLentry *) _a)[one]); + MVLentry *second = &(((MVLentry *) _a)[two]); + memcpy(&tmp, first, sizeof (MVLentry)); + memcpy(first, second, sizeof (MVLentry)); + memcpy(second, &tmp, sizeof (MVLentry)); + } /* if */ +} /* mvl_entry_swap */ + + +static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 location = 8; /* sizeof sig. */ + MVLentry *entry; + + BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + location += (17 * fileCount); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = location; + location += entry->size; + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + mvl_entry_cmp, mvl_entry_swap); + return(1); +} /* mvl_load_entries */ + + +static void *MVL_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (MVLinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed); + if (!mvl_load_entries(name, forWriting, info)) + goto MVL_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +MVL_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* MVL_openArchive */ + + +static void MVL_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in MVL files. */ + if (*dname == '\0') + { + MVLinfo *info = ((MVLinfo *) opaque); + MVLentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* MVL_enumerateFiles */ + + +static MVLentry *mvl_find_entry(MVLinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + MVLentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_stricmpASCII(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* mvl_find_entry */ + + +static int MVL_exists(dvoid *opaque, const char *name) +{ + return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL); +} /* MVL_exists */ + + +static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* MVL_isDirectory */ + + +static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* MVL_isSymLink */ + + +static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (mvl_find_entry(info, name) != NULL); + if (*fileExists) /* use time of MVL itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* MVL_getLastModTime */ + + +static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) opaque); + MVLfileinfo *finfo; + MVLentry *entry; + + entry = mvl_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* MVL_openRead */ + + +static fvoid *MVL_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openWrite */ + + +static fvoid *MVL_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openAppend */ + + +static int MVL_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_remove */ + + +static int MVL_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL = +{ + "MVL", + MVL_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_MVL = +{ + &__PHYSFS_ArchiveInfo_MVL, + MVL_isArchive, /* isArchive() method */ + MVL_openArchive, /* openArchive() method */ + MVL_enumerateFiles, /* enumerateFiles() method */ + MVL_exists, /* exists() method */ + MVL_isDirectory, /* isDirectory() method */ + MVL_isSymLink, /* isSymLink() method */ + MVL_getLastModTime, /* getLastModTime() method */ + MVL_openRead, /* openRead() method */ + MVL_openWrite, /* openWrite() method */ + MVL_openAppend, /* openAppend() method */ + MVL_remove, /* remove() method */ + MVL_mkdir, /* mkdir() method */ + MVL_dirClose, /* dirClose() method */ + MVL_read, /* read() method */ + MVL_write, /* write() method */ + MVL_eof, /* eof() method */ + MVL_tell, /* tell() method */ + MVL_seek, /* seek() method */ + MVL_fileLength, /* fileLength() method */ + MVL_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_MVL */ + +/* end of mvl.c ... */ + diff --git a/3rdparty/physfs/archivers/qpak.c b/3rdparty/physfs/archivers/qpak.c new file mode 100644 index 0000000..1aa7a32 --- /dev/null +++ b/3rdparty/physfs/archivers/qpak.c @@ -0,0 +1,633 @@ +/* + * QPAK support routines for PhysicsFS. + * + * This archiver handles the archive format utilized by Quake 1 and 2. + * Quake3-based games use the PkZip/Info-Zip format (which our zip.c + * archiver handles). + * + * ======================================================================== + * + * This format info (in more detail) comes from: + * http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt + * + * Quake PAK Format + * + * Header + * (4 bytes) signature = 'PACK' + * (4 bytes) directory offset + * (4 bytes) directory length + * + * Directory + * (56 bytes) file name + * (4 bytes) file position + * (4 bytes) file length + * + * ======================================================================== + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#if (defined PHYSFS_SUPPORTS_QPAK) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if 1 /* Make this case insensitive? */ +#define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y) +#define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z) +#else +#define QPAK_strcmp(x, y) strcmp(x, y) +#define QPAK_strncmp(x, y, z) strncmp(x, y, z) +#endif + + +typedef struct +{ + char name[56]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} QPAKentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + QPAKentry *entries; +} QPAKinfo; + +typedef struct +{ + void *handle; + QPAKentry *entry; + PHYSFS_uint32 curPos; +} QPAKfileinfo; + +/* Magic numbers... */ +#define QPAK_SIG 0x4b434150 /* "PACK" in ASCII. */ + + +static void QPAK_dirClose(dvoid *opaque) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* QPAK_dirClose */ + + +static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* QPAK_read */ + + +static PHYSFS_sint64 QPAK_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* QPAK_write */ + + +static int QPAK_eof(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* QPAK_eof */ + + +static PHYSFS_sint64 QPAK_tell(fvoid *opaque) +{ + return(((QPAKfileinfo *) opaque)->curPos); +} /* QPAK_tell */ + + +static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* QPAK_seek */ + + +static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* QPAK_fileLength */ + + +static int QPAK_fileClose(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* QPAK_fileClose */ + + +static int qpak_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint32 buf; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + buf = PHYSFS_swapULE32(buf); + GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed); + + if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + buf = PHYSFS_swapULE32(buf); /* directory table offset. */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + *count = PHYSFS_swapULE32(*count); + + /* corrupted archive? */ + GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed); + + if (!__PHYSFS_platformSeek(*fh, buf)) + goto openQpak_failed; + + *count /= 64; + return(1); + +openQpak_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* qpak_open */ + + +static int QPAK_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = qpak_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* QPAK_isArchive */ + + +static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const QPAKentry *a = (const QPAKentry *) _a; + return(QPAK_strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* qpak_entry_cmp */ + + +static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + QPAKentry tmp; + QPAKentry *first = &(((QPAKentry *) _a)[one]); + QPAKentry *second = &(((QPAKentry *) _a)[two]); + memcpy(&tmp, first, sizeof (QPAKentry)); + memcpy(first, second, sizeof (QPAKentry)); + memcpy(second, &tmp, sizeof (QPAKentry)); + } /* if */ +} /* qpak_entry_swap */ + + +static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + QPAKentry *entry; + + BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + PHYSFS_uint32 loc; + + if (__PHYSFS_platformRead(fh,&entry->name,sizeof(entry->name),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh,&loc,sizeof(loc),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh,&entry->size,sizeof(entry->size),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = PHYSFS_swapULE32(loc); + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + qpak_entry_cmp, qpak_entry_swap); + return(1); +} /* qpak_load_entries */ + + +static void *QPAK_openArchive(const char *name, int forWriting) +{ + QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo)); + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (QPAKinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + if (info->filename == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto QPAK_openArchive_failed; + } /* if */ + + if (!qpak_load_entries(name, forWriting, info)) + goto QPAK_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +QPAK_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* QPAK_openArchive */ + + +static PHYSFS_sint32 qpak_find_start_of_dir(QPAKinfo *info, const char *path, + int stop_on_first_find) +{ + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + PHYSFS_uint32 dlen = strlen(path); + PHYSFS_sint32 retval = -1; + const char *name; + int rc; + + if (*path == '\0') /* root dir? */ + return(0); + + if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + name = info->entries[middle].name; + rc = QPAK_strncmp(path, name, dlen); + if (rc == 0) + { + char ch = name[dlen]; + if (ch < '/') /* make sure this isn't just a substr match. */ + rc = -1; + else if (ch > '/') + rc = 1; + else + { + if (stop_on_first_find) /* Just checking dir's existance? */ + return(middle); + + if (name[dlen + 1] == '\0') /* Skip initial dir entry. */ + return(middle + 1); + + /* there might be more entries earlier in the list. */ + retval = middle; + hi = middle - 1; + } /* else */ + } /* if */ + + if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + return(retval); +} /* qpak_find_start_of_dir */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, PHYSFS_sint32 ln) +{ + char *newstr = __PHYSFS_smallAlloc(ln + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, ln); + newstr[ln] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void QPAK_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + PHYSFS_sint32 dlen, dlen_inc, max, i; + + i = qpak_find_start_of_dir(info, dname, 0); + if (i == -1) /* no such directory. */ + return; + + dlen = strlen(dname); + if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; + max = (PHYSFS_sint32) info->entryCount; + while (i < max) + { + char *add; + char *ptr; + PHYSFS_sint32 ln; + char *e = info->entries[i].name; + if ((dlen) && ((QPAK_strncmp(e, dname, dlen)) || (e[dlen] != '/'))) + break; /* past end of this dir; we're done. */ + + add = e + dlen_inc; + ptr = strchr(add, '/'); + ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add)); + doEnumCallback(cb, callbackdata, origdir, add, ln); + ln += dlen_inc; /* point past entry to children... */ + + /* increment counter and skip children of subdirs... */ + while ((++i < max) && (ptr != NULL)) + { + char *e_new = info->entries[i].name; + if ((QPAK_strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/')) + break; + } /* while */ + } /* while */ +} /* QPAK_enumerateFiles */ + + +/* + * This will find the QPAKentry associated with a path in platform-independent + * notation. Directories don't have QPAKentries associated with them, but + * (*isDir) will be set to non-zero if a dir was hit. + */ +static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir) +{ + QPAKentry *a = info->entries; + PHYSFS_sint32 pathlen = strlen(path); + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + const char *thispath = NULL; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + thispath = a[middle].name; + rc = QPAK_strncmp(path, thispath, pathlen); + + if (rc > 0) + lo = middle + 1; + + else if (rc < 0) + hi = middle - 1; + + else /* substring match...might be dir or entry or nothing. */ + { + if (isDir != NULL) + { + *isDir = (thispath[pathlen] == '/'); + if (*isDir) + return(NULL); + } /* if */ + + if (thispath[pathlen] == '\0') /* found entry? */ + return(&a[middle]); + /* adjust search params, try again. */ + else if (thispath[pathlen] > '/') + hi = middle - 1; + else + lo = middle + 1; + } /* if */ + } /* while */ + + if (isDir != NULL) + *isDir = 0; + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* qpak_find_entry */ + + +static int QPAK_exists(dvoid *opaque, const char *name) +{ + int isDir; + QPAKinfo *info = (QPAKinfo *) opaque; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + return((entry != NULL) || (isDir)); +} /* QPAK_exists */ + + +static int QPAK_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + QPAKinfo *info = (QPAKinfo *) opaque; + int isDir; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* definitely a dir. */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, 0); +} /* QPAK_isDirectory */ + + +static int QPAK_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = QPAK_exists(opaque, name); + return(0); /* never symlinks in a quake pak. */ +} /* QPAK_isSymLink */ + + +static PHYSFS_sint64 QPAK_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + int isDir; + QPAKinfo *info = ((QPAKinfo *) opaque); + PHYSFS_sint64 retval = -1; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (*fileExists) /* use time of QPAK itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* QPAK_getLastModTime */ + + +static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + QPAKfileinfo *finfo; + QPAKentry *entry; + int isDir; + + entry = qpak_find_entry(info, fnm, &isDir); + *fileExists = ((entry != NULL) || (isDir)); + BAIL_IF_MACRO(isDir, ERR_NOT_A_FILE, NULL); + BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL); + + finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* QPAK_openRead */ + + +static fvoid *QPAK_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* QPAK_openWrite */ + + +static fvoid *QPAK_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* QPAK_openAppend */ + + +static int QPAK_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* QPAK_remove */ + + +static int QPAK_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* QPAK_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK = +{ + "PAK", + QPAK_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = +{ + &__PHYSFS_ArchiveInfo_QPAK, + QPAK_isArchive, /* isArchive() method */ + QPAK_openArchive, /* openArchive() method */ + QPAK_enumerateFiles, /* enumerateFiles() method */ + QPAK_exists, /* exists() method */ + QPAK_isDirectory, /* isDirectory() method */ + QPAK_isSymLink, /* isSymLink() method */ + QPAK_getLastModTime, /* getLastModTime() method */ + QPAK_openRead, /* openRead() method */ + QPAK_openWrite, /* openWrite() method */ + QPAK_openAppend, /* openAppend() method */ + QPAK_remove, /* remove() method */ + QPAK_mkdir, /* mkdir() method */ + QPAK_dirClose, /* dirClose() method */ + QPAK_read, /* read() method */ + QPAK_write, /* write() method */ + QPAK_eof, /* eof() method */ + QPAK_tell, /* tell() method */ + QPAK_seek, /* seek() method */ + QPAK_fileLength, /* fileLength() method */ + QPAK_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_QPAK */ + +/* end of qpak.c ... */ + diff --git a/3rdparty/physfs/archivers/wad.c b/3rdparty/physfs/archivers/wad.c new file mode 100644 index 0000000..052792c --- /dev/null +++ b/3rdparty/physfs/archivers/wad.c @@ -0,0 +1,531 @@ +/* + * WAD support routines for PhysicsFS. + * + * This driver handles DOOM engine archives ("wads"). + * This format (but not this driver) was designed by id Software for use + * with the DOOM engine. + * The specs of the format are from the unofficial doom specs v1.666 + * found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html + * The format of the archive: (from the specs) + * + * A WAD file has three parts: + * (1) a twelve-byte header + * (2) one or more "lumps" + * (3) a directory or "info table" that contains the names, offsets, and + * sizes of all the lumps in the WAD + * + * The header consists of three four-byte parts: + * (a) an ASCII string which must be either "IWAD" or "PWAD" + * (b) a 4-byte (long) integer which is the number of lumps in the wad + * (c) a long integer which is the file offset to the start of + * the directory + * + * The directory has one 16-byte entry for every lump. Each entry consists + * of three parts: + * + * (a) a long integer, the file offset to the start of the lump + * (b) a long integer, the size of the lump in bytes + * (c) an 8-byte ASCII string, the name of the lump, padded with zeros. + * For example, the "DEMO1" entry in hexadecimal would be + * (44 45 4D 4F 31 00 00 00) + * + * Note that there is no way to tell if an opened WAD archive is a + * IWAD or PWAD with this archiver. + * I couldn't think of a way to provide that information, without being too + * hacky. + * I don't think it's really that important though. + * + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Travis Wells, based on the GRP archiver by + * Ryan C. Gordon. + */ + +#if (defined PHYSFS_SUPPORTS_WAD) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[18]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} WADentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + PHYSFS_uint32 entryOffset; + WADentry *entries; +} WADinfo; + +typedef struct +{ + void *handle; + WADentry *entry; + PHYSFS_uint32 curPos; +} WADfileinfo; + + +static void WAD_dirClose(dvoid *opaque) +{ + WADinfo *info = ((WADinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* WAD_dirClose */ + + +static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* WAD_read */ + + +static PHYSFS_sint64 WAD_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* WAD_write */ + + +static int WAD_eof(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* WAD_eof */ + + +static PHYSFS_sint64 WAD_tell(fvoid *opaque) +{ + return(((WADfileinfo *) opaque)->curPos); +} /* WAD_tell */ + + +static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* WAD_seek */ + + +static PHYSFS_sint64 WAD_fileLength(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* WAD_fileLength */ + + +static int WAD_fileClose(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* WAD_fileClose */ + + +static int wad_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset) +{ + PHYSFS_uint8 buf[4]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1) + goto openWad_failed; + + if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openWad_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openWad_failed; + + *count = PHYSFS_swapULE32(*count); + + if (__PHYSFS_platformRead(*fh, offset, sizeof (PHYSFS_uint32), 1) != 1) + goto openWad_failed; + + *offset = PHYSFS_swapULE32(*offset); + + return(1); + +openWad_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* wad_open */ + + +static int WAD_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount,offset; + int retval = wad_open(filename, forWriting, &fh, &fileCount,&offset); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* WAD_isArchive */ + + +static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const WADentry *a = (const WADentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* wad_entry_cmp */ + + +static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + WADentry tmp; + WADentry *first = &(((WADentry *) _a)[one]); + WADentry *second = &(((WADentry *) _a)[two]); + memcpy(&tmp, first, sizeof (WADentry)); + memcpy(first, second, sizeof (WADentry)); + memcpy(second, &tmp, sizeof (WADentry)); + } /* if */ +} /* wad_entry_swap */ + + +static int wad_load_entries(const char *name, int forWriting, WADinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 directoryOffset; + WADentry *entry; + + BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0); + info->entryCount = fileCount; + info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + __PHYSFS_platformSeek(fh,directoryOffset); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->startPos, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->name, 8, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->name[8] = '\0'; /* name might not be null-terminated in file. */ + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = PHYSFS_swapULE32(entry->startPos); + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + wad_entry_cmp, wad_entry_swap); + return(1); +} /* wad_load_entries */ + + +static void *WAD_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (WADinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed); + + if (!wad_load_entries(name, forWriting, info)) + goto WAD_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +WAD_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* WAD_openArchive */ + + +static void WAD_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + WADinfo *info = ((WADinfo *) opaque); + WADentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + const char *name; + char *sep; + + if (*dname == '\0') /* root directory enumeration? */ + { + for (i = 0; i < max; i++, entry++) + { + name = entry->name; + if (strchr(name, '/') == NULL) + cb(callbackdata, origdir, name); + } /* for */ + } /* if */ + else + { + for (i = 0; i < max; i++, entry++) + { + name = entry->name; + sep = strchr(name, '/'); + if (sep != NULL) + { + if (strncmp(dname, name, (sep - name)) == 0) + cb(callbackdata, origdir, sep + 1); + } /* if */ + } /* for */ + } /* else */ +} /* WAD_enumerateFiles */ + + +static WADentry *wad_find_entry(WADinfo *info, const char *name) +{ + WADentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = strcmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* wad_find_entry */ + + +static int WAD_exists(dvoid *opaque, const char *name) +{ + return(wad_find_entry(((WADinfo *) opaque), name) != NULL); +} /* WAD_exists */ + + +static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + WADentry *entry = wad_find_entry(((WADinfo *) opaque), name); + if (entry != NULL) + { + char *n; + + *fileExists = 1; + + /* Can't be a directory if it's a subdirectory. */ + if (strchr(entry->name, '/') != NULL) + return(0); + + /* Check if it matches "MAP??" or "E?M?" ... */ + n = entry->name; + if ((n[0] == 'E' && n[2] == 'M') || + (n[0] == 'M' && n[1] == 'A' && n[2] == 'P' && n[6] == 0)) + { + return(1); + } /* if */ + return(0); + } /* if */ + else + { + *fileExists = 0; + return(0); + } /* else */ +} /* WAD_isDirectory */ + + +static int WAD_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = WAD_exists(opaque, name); + return(0); /* never symlinks in a wad. */ +} /* WAD_isSymLink */ + + +static PHYSFS_sint64 WAD_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + WADinfo *info = ((WADinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (wad_find_entry(info, name) != NULL); + if (*fileExists) /* use time of WAD itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* WAD_getLastModTime */ + + +static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + WADinfo *info = ((WADinfo *) opaque); + WADfileinfo *finfo; + WADentry *entry; + + entry = wad_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* WAD_openRead */ + + +static fvoid *WAD_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* WAD_openWrite */ + + +static fvoid *WAD_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* WAD_openAppend */ + + +static int WAD_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* WAD_remove */ + + +static int WAD_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* WAD_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD = +{ + "WAD", + WAD_ARCHIVE_DESCRIPTION, + "Travis Wells ", + "http://www.3dmm2.com/doom/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_WAD = +{ + &__PHYSFS_ArchiveInfo_WAD, + WAD_isArchive, /* isArchive() method */ + WAD_openArchive, /* openArchive() method */ + WAD_enumerateFiles, /* enumerateFiles() method */ + WAD_exists, /* exists() method */ + WAD_isDirectory, /* isDirectory() method */ + WAD_isSymLink, /* isSymLink() method */ + WAD_getLastModTime, /* getLastModTime() method */ + WAD_openRead, /* openRead() method */ + WAD_openWrite, /* openWrite() method */ + WAD_openAppend, /* openAppend() method */ + WAD_remove, /* remove() method */ + WAD_mkdir, /* mkdir() method */ + WAD_dirClose, /* dirClose() method */ + WAD_read, /* read() method */ + WAD_write, /* write() method */ + WAD_eof, /* eof() method */ + WAD_tell, /* tell() method */ + WAD_seek, /* seek() method */ + WAD_fileLength, /* fileLength() method */ + WAD_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_WAD */ + +/* end of wad.c ... */ + diff --git a/3rdparty/physfs/archivers/zip.c b/3rdparty/physfs/archivers/zip.c new file mode 100644 index 0000000..54cd7c3 --- /dev/null +++ b/3rdparty/physfs/archivers/zip.c @@ -0,0 +1,1472 @@ +/* + * ZIP support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, with some peeking at "unzip.c" + * by Gilles Vollant. + */ + +#if (defined PHYSFS_SUPPORTS_ZIP) + +#include +#include +#include +#ifndef _WIN32_WCE +#include +#include +#endif +#include "physfs.h" +#include "zlib.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* + * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened, + * and is freed when you close the file; compressed data is read into + * this buffer, and then is decompressed into the buffer passed to + * PHYSFS_read(). + * + * Uncompressed entries in a zipfile do not allocate this buffer; they just + * read data directly into the buffer passed to PHYSFS_read(). + * + * Depending on your speed and memory requirements, you should tweak this + * value. + */ +#define ZIP_READBUFSIZE (16 * 1024) + + +/* + * Entries are "unresolved" until they are first opened. At that time, + * local file headers parsed/validated, data offsets will be updated to look + * at the actual file data instead of the header, and symlinks will be + * followed and optimized. This means that we don't seek and read around the + * archive until forced to do so, and after the first time, we had to do + * less reading and parsing, which is very CD-ROM friendly. + */ +typedef enum +{ + ZIP_UNRESOLVED_FILE, + ZIP_UNRESOLVED_SYMLINK, + ZIP_RESOLVING, + ZIP_RESOLVED, + ZIP_BROKEN_FILE, + ZIP_BROKEN_SYMLINK +} ZipResolveType; + + +/* + * One ZIPentry is kept for each file in an open ZIP archive. + */ +typedef struct _ZIPentry +{ + char *name; /* Name of file in archive */ + struct _ZIPentry *symlink; /* NULL or file we symlink to */ + ZipResolveType resolved; /* Have we resolved file/symlink? */ + PHYSFS_uint32 offset; /* offset of data in archive */ + PHYSFS_uint16 version; /* version made by */ + PHYSFS_uint16 version_needed; /* version needed to extract */ + PHYSFS_uint16 compression_method; /* compression method */ + PHYSFS_uint32 crc; /* crc-32 */ + PHYSFS_uint32 compressed_size; /* compressed size */ + PHYSFS_uint32 uncompressed_size; /* uncompressed size */ + PHYSFS_sint64 last_mod_time; /* last file mod time */ +} ZIPentry; + +/* + * One ZIPinfo is kept for each open ZIP archive. + */ +typedef struct +{ + char *archiveName; /* path to ZIP in platform-dependent notation. */ + PHYSFS_uint16 entryCount; /* Number of files in ZIP. */ + ZIPentry *entries; /* info on all files in ZIP. */ +} ZIPinfo; + +/* + * One ZIPfileinfo is kept for each open file in a ZIP archive. + */ +typedef struct +{ + ZIPentry *entry; /* Info on file. */ + void *handle; /* physical file handle. */ + PHYSFS_uint32 compressed_position; /* offset in compressed data. */ + PHYSFS_uint32 uncompressed_position; /* tell() position. */ + PHYSFS_uint8 *buffer; /* decompression buffer. */ + z_stream stream; /* zlib stream state. */ +} ZIPfileinfo; + + +/* Magic numbers... */ +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 + +/* compression methods... */ +#define COMPMETH_NONE 0 +/* ...and others... */ + + +#define UNIX_FILETYPE_MASK 0170000 +#define UNIX_FILETYPE_SYMLINK 0120000 + + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) +{ + return(((PHYSFS_Allocator *) opaque)->Malloc(items * size)); +} /* zlibPhysfsAlloc */ + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static void zlibPhysfsFree(voidpf opaque, voidpf address) +{ + ((PHYSFS_Allocator *) opaque)->Free(address); +} /* zlibPhysfsFree */ + + +/* + * Construct a new z_stream to a sane state. + */ +static void initializeZStream(z_stream *pstr) +{ + memset(pstr, '\0', sizeof (z_stream)); + pstr->zalloc = zlibPhysfsAlloc; + pstr->zfree = zlibPhysfsFree; + pstr->opaque = &allocator; +} /* initializeZStream */ + + +static const char *zlib_error_string(int rc) +{ + switch (rc) + { + case Z_OK: return(NULL); /* not an error. */ + case Z_STREAM_END: return(NULL); /* not an error. */ +#ifndef _WIN32_WCE + case Z_ERRNO: return(strerror(errno)); +#endif + case Z_NEED_DICT: return(ERR_NEED_DICT); + case Z_DATA_ERROR: return(ERR_DATA_ERROR); + case Z_MEM_ERROR: return(ERR_MEMORY_ERROR); + case Z_BUF_ERROR: return(ERR_BUFFER_ERROR); + case Z_VERSION_ERROR: return(ERR_VERSION_ERROR); + default: return(ERR_UNKNOWN_ERROR); + } /* switch */ + + return(NULL); +} /* zlib_error_string */ + + +/* + * Wrap all zlib calls in this, so the physfs error state is set appropriately. + */ +static int zlib_err(int rc) +{ + const char *str = zlib_error_string(rc); + if (str != NULL) + __PHYSFS_setError(str); + return(rc); +} /* zlib_err */ + + +/* + * Read an unsigned 32-bit int and swap to native byte order. + */ +static int readui32(void *in, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE32(v); + return(1); +} /* readui32 */ + + +/* + * Read an unsigned 16-bit int and swap to native byte order. + */ +static int readui16(void *in, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE16(v); + return(1); +} /* readui16 */ + + +static PHYSFS_sint64 ZIP_read(fvoid *opaque, void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + ZIPentry *entry = finfo->entry; + PHYSFS_sint64 retval = 0; + PHYSFS_sint64 maxread = ((PHYSFS_sint64) objSize) * objCount; + PHYSFS_sint64 avail = entry->uncompressed_size - + finfo->uncompressed_position; + + BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */ + + if (avail < maxread) + { + maxread = avail - (avail % objSize); + objCount = (PHYSFS_uint32) (maxread / objSize); + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ + } /* if */ + + if (entry->compression_method == COMPMETH_NONE) + { + retval = __PHYSFS_platformRead(finfo->handle, buf, objSize, objCount); + } /* if */ + + else + { + finfo->stream.next_out = buf; + finfo->stream.avail_out = objSize * objCount; + + while (retval < maxread) + { + PHYSFS_uint32 before = finfo->stream.total_out; + int rc; + + if (finfo->stream.avail_in == 0) + { + PHYSFS_sint64 br; + + br = entry->compressed_size - finfo->compressed_position; + if (br > 0) + { + if (br > ZIP_READBUFSIZE) + br = ZIP_READBUFSIZE; + + br = __PHYSFS_platformRead(finfo->handle, + finfo->buffer, + 1, (PHYSFS_uint32) br); + if (br <= 0) + break; + + finfo->compressed_position += (PHYSFS_uint32) br; + finfo->stream.next_in = finfo->buffer; + finfo->stream.avail_in = (PHYSFS_uint32) br; + } /* if */ + } /* if */ + + rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); + retval += (finfo->stream.total_out - before); + + if (rc != Z_OK) + break; + } /* while */ + + retval /= objSize; + } /* else */ + + if (retval > 0) + finfo->uncompressed_position += (PHYSFS_uint32) (retval * objSize); + + return(retval); +} /* ZIP_read */ + + +static PHYSFS_sint64 ZIP_write(fvoid *opaque, const void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* ZIP_write */ + + +static int ZIP_eof(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + return(finfo->uncompressed_position >= finfo->entry->uncompressed_size); +} /* ZIP_eof */ + + +static PHYSFS_sint64 ZIP_tell(fvoid *opaque) +{ + return(((ZIPfileinfo *) opaque)->uncompressed_position); +} /* ZIP_tell */ + + +static int ZIP_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + ZIPentry *entry = finfo->entry; + void *in = finfo->handle; + + BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0); + + if (entry->compression_method == COMPMETH_NONE) + { + PHYSFS_sint64 newpos = offset + entry->offset; + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0); + finfo->uncompressed_position = (PHYSFS_uint32) offset; + } /* if */ + + else + { + /* + * If seeking backwards, we need to redecode the file + * from the start and throw away the compressed bits until we hit + * the offset we need. If seeking forward, we still need to + * decode, but we don't rewind first. + */ + if (offset < finfo->uncompressed_position) + { + /* we do a copy so state is sane if inflateInit2() fails. */ + z_stream str; + initializeZStream(&str); + if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK) + return(0); + + if (!__PHYSFS_platformSeek(in, entry->offset)) + return(0); + + inflateEnd(&finfo->stream); + memcpy(&finfo->stream, &str, sizeof (z_stream)); + finfo->uncompressed_position = finfo->compressed_position = 0; + } /* if */ + + while (finfo->uncompressed_position != offset) + { + PHYSFS_uint8 buf[512]; + PHYSFS_uint32 maxread; + + maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position); + if (maxread > sizeof (buf)) + maxread = sizeof (buf); + + if (ZIP_read(finfo, buf, maxread, 1) != 1) + return(0); + } /* while */ + } /* else */ + + return(1); +} /* ZIP_seek */ + + +static PHYSFS_sint64 ZIP_fileLength(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + return(finfo->entry->uncompressed_size); +} /* ZIP_fileLength */ + + +static int ZIP_fileClose(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + + if (finfo->entry->compression_method != COMPMETH_NONE) + inflateEnd(&finfo->stream); + + if (finfo->buffer != NULL) + allocator.Free(finfo->buffer); + + allocator.Free(finfo); + return(1); +} /* ZIP_fileClose */ + + +static PHYSFS_sint64 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len) +{ + PHYSFS_uint8 buf[256]; + PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 }; + PHYSFS_sint32 i = 0; + PHYSFS_sint64 filelen; + PHYSFS_sint64 filepos; + PHYSFS_sint32 maxread; + PHYSFS_sint32 totalread = 0; + int found = 0; + + filelen = __PHYSFS_platformFileLength(in); + BAIL_IF_MACRO(filelen == -1, NULL, -1); /* !!! FIXME: unlocalized string */ + BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 2 gigs?!", -1); + + /* + * Jump to the end of the file and start reading backwards. + * The last thing in the file is the zipfile comment, which is variable + * length, and the field that specifies its size is before it in the + * file (argh!)...this means that we need to scan backwards until we + * hit the end-of-central-dir signature. We can then sanity check that + * the comment was as big as it should be to make sure we're in the + * right place. The comment length field is 16 bits, so we can stop + * searching for that signature after a little more than 64k at most, + * and call it a corrupted zipfile. + */ + + if (sizeof (buf) < filelen) + { + filepos = filelen - sizeof (buf); + maxread = sizeof (buf); + } /* if */ + else + { + filepos = 0; + maxread = (PHYSFS_uint32) filelen; + } /* else */ + + while ((totalread < filelen) && (totalread < 65557)) + { + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1); + + /* make sure we catch a signature between buffers. */ + if (totalread != 0) + { + if (__PHYSFS_platformRead(in, buf, maxread - 4, 1) != 1) + return(-1); + memcpy(&buf[maxread - 4], &extra, sizeof (extra)); + totalread += maxread - 4; + } /* if */ + else + { + if (__PHYSFS_platformRead(in, buf, maxread, 1) != 1) + return(-1); + totalread += maxread; + } /* else */ + + memcpy(&extra, buf, sizeof (extra)); + + for (i = maxread - 4; i > 0; i--) + { + if ((buf[i + 0] == 0x50) && + (buf[i + 1] == 0x4B) && + (buf[i + 2] == 0x05) && + (buf[i + 3] == 0x06) ) + { + found = 1; /* that's the signature! */ + break; + } /* if */ + } /* for */ + + if (found) + break; + + filepos -= (maxread - 4); + if (filepos < 0) + filepos = 0; + } /* while */ + + BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1); + + if (len != NULL) + *len = filelen; + + return(filepos + i); +} /* zip_find_end_of_central_dir */ + + +static int ZIP_isArchive(const char *filename, int forWriting) +{ + PHYSFS_uint32 sig; + int retval = 0; + void *in; + + in = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(in == NULL, NULL, 0); + + /* + * The first thing in a zip file might be the signature of the + * first local file record, so it makes for a quick determination. + */ + if (readui32(in, &sig)) + { + retval = (sig == ZIP_LOCAL_FILE_SIG); + if (!retval) + { + /* + * No sig...might be a ZIP with data at the start + * (a self-extracting executable, etc), so we'll have to do + * it the hard way... + */ + retval = (zip_find_end_of_central_dir(in, NULL) != -1); + } /* if */ + } /* if */ + + __PHYSFS_platformClose(in); + return(retval); +} /* ZIP_isArchive */ + + +static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max) +{ + PHYSFS_uint32 i; + for (i = 0; i < max; i++) + { + ZIPentry *entry = &entries[i]; + if (entry->name != NULL) + allocator.Free(entry->name); + } /* for */ + + allocator.Free(entries); +} /* zip_free_entries */ + + +/* + * This will find the ZIPentry associated with a path in platform-independent + * notation. Directories don't have ZIPentries associated with them, but + * (*isDir) will be set to non-zero if a dir was hit. + */ +static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir) +{ + ZIPentry *a = info->entries; + PHYSFS_sint32 pathlen = strlen(path); + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + const char *thispath = NULL; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + thispath = a[middle].name; + rc = strncmp(path, thispath, pathlen); + + if (rc > 0) + lo = middle + 1; + + else if (rc < 0) + hi = middle - 1; + + else /* substring match...might be dir or entry or nothing. */ + { + int i; + + if (isDir != NULL) + { + *isDir = (thispath[pathlen] == '/'); + if (*isDir) + return(NULL); + } /* if */ + + if (thispath[pathlen] == '\0') /* found entry? */ + return(&a[middle]); + + /* substring match; search remaining space to find it... */ + for (i = lo; i < hi; i++) + { + thispath = a[i].name; + if (strncmp(path, thispath, pathlen) == 0) + { + if (isDir != NULL) + { + *isDir = (thispath[pathlen] == '/'); + if (*isDir) + return(NULL); + } /* if */ + + if (thispath[pathlen] == '\0') /* found entry? */ + return(&a[i]); + } /* if */ + } /* for */ + break; + + } /* else */ + } /* while */ + + if (isDir != NULL) + *isDir = 0; + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* zip_find_entry */ + + +/* Convert paths from old, buggy DOS zippers... */ +static void zip_convert_dos_path(ZIPentry *entry, char *path) +{ + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF); + if (hosttype == 0) /* FS_FAT_ */ + { + while (*path) + { + if (*path == '\\') + *path = '/'; + path++; + } /* while */ + } /* if */ +} /* zip_convert_dos_path */ + + +static void zip_expand_symlink_path(char *path) +{ + char *ptr = path; + char *prevptr = path; + + while (1) + { + ptr = strchr(ptr, '/'); + if (ptr == NULL) + break; + + if (*(ptr + 1) == '.') + { + if (*(ptr + 2) == '/') + { + /* current dir in middle of string: ditch it. */ + memmove(ptr, ptr + 2, strlen(ptr + 2) + 1); + } /* else if */ + + else if (*(ptr + 2) == '\0') + { + /* current dir at end of string: ditch it. */ + *ptr = '\0'; + } /* else if */ + + else if (*(ptr + 2) == '.') + { + if (*(ptr + 3) == '/') + { + /* parent dir in middle: move back one, if possible. */ + memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1); + ptr = prevptr; + while (prevptr != path) + { + prevptr--; + if (*prevptr == '/') + { + prevptr++; + break; + } /* if */ + } /* while */ + } /* if */ + + if (*(ptr + 3) == '\0') + { + /* parent dir at end: move back one, if possible. */ + *prevptr = '\0'; + } /* if */ + } /* if */ + } /* if */ + else + { + prevptr = ptr; + ptr++; + } /* else */ + } /* while */ +} /* zip_expand_symlink_path */ + +/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */ +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry); + +/* + * Look for the entry named by (path). If it exists, resolve it, and return + * a pointer to that entry. If it's another symlink, keep resolving until you + * hit a real file and then return a pointer to the final non-symlink entry. + * If there's a problem, return NULL. (path) is always free()'d by this + * function. + */ +static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path) +{ + ZIPentry *entry; + + zip_expand_symlink_path(path); + entry = zip_find_entry(info, path, NULL); + if (entry != NULL) + { + if (!zip_resolve(in, info, entry)) /* recursive! */ + entry = NULL; + else + { + if (entry->symlink != NULL) + entry = entry->symlink; + } /* else */ + } /* if */ + + allocator.Free(path); + return(entry); +} /* zip_follow_symlink */ + + +static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry) +{ + char *path; + PHYSFS_uint32 size = entry->uncompressed_size; + int rc = 0; + + /* + * We've already parsed the local file header of the symlink at this + * point. Now we need to read the actual link from the file data and + * follow it. + */ + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); + + path = (char *) allocator.Malloc(size + 1); + BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0); + + if (entry->compression_method == COMPMETH_NONE) + rc = (__PHYSFS_platformRead(in, path, size, 1) == 1); + + else /* symlink target path is compressed... */ + { + z_stream stream; + PHYSFS_uint32 complen = entry->compressed_size; + PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen); + if (compressed != NULL) + { + if (__PHYSFS_platformRead(in, compressed, complen, 1) == 1) + { + initializeZStream(&stream); + stream.next_in = compressed; + stream.avail_in = complen; + stream.next_out = (unsigned char *) path; + stream.avail_out = size; + if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) + { + rc = zlib_err(inflate(&stream, Z_FINISH)); + inflateEnd(&stream); + + /* both are acceptable outcomes... */ + rc = ((rc == Z_OK) || (rc == Z_STREAM_END)); + } /* if */ + } /* if */ + __PHYSFS_smallFree(compressed); + } /* if */ + } /* else */ + + if (!rc) + allocator.Free(path); + else + { + path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ + zip_convert_dos_path(entry, path); + entry->symlink = zip_follow_symlink(in, info, path); + } /* else */ + + return(entry->symlink != NULL); +} /* zip_resolve_symlink */ + + +/* + * Parse the local file header of an entry, and update entry->offset. + */ +static int zip_parse_local(void *in, ZIPentry *entry) +{ + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_uint16 fnamelen; + PHYSFS_uint16 extralen; + + /* + * crc and (un)compressed_size are always zero if this is a "JAR" + * archive created with Sun's Java tools, apparently. We only + * consider this archive corrupted if those entries don't match and + * aren't zero. That seems to work well. + */ + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + + entry->offset += fnamelen + extralen + 30; + return(1); +} /* zip_parse_local */ + + +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry) +{ + int retval = 1; + ZipResolveType resolve_type = entry->resolved; + + /* Don't bother if we've failed to resolve this entry before. */ + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0); + + /* uhoh...infinite symlink loop! */ + BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0); + + /* + * We fix up the offset to point to the actual data on the + * first open, since we don't want to seek across the whole file on + * archive open (can be SLOW on large, CD-stored files), but we + * need to check the local file header...not just for corruption, + * but since it stores offset info the central directory does not. + */ + if (resolve_type != ZIP_RESOLVED) + { + entry->resolved = ZIP_RESOLVING; + + retval = zip_parse_local(in, entry); + if (retval) + { + /* + * If it's a symlink, find the original file. This will cause + * resolution of other entries (other symlinks and, eventually, + * the real file) if all goes well. + */ + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + retval = zip_resolve_symlink(in, info, entry); + } /* if */ + + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK); + else if (resolve_type == ZIP_UNRESOLVED_FILE) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); + } /* if */ + + return(retval); +} /* zip_resolve */ + + +static int zip_version_does_symlinks(PHYSFS_uint32 version) +{ + int retval = 0; + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); + + switch (hosttype) + { + /* + * These are the platforms that can NOT build an archive with + * symlinks, according to the Info-ZIP project. + */ + case 0: /* FS_FAT_ */ + case 1: /* AMIGA_ */ + case 2: /* VMS_ */ + case 4: /* VM_CSM_ */ + case 6: /* FS_HPFS_ */ + case 11: /* FS_NTFS_ */ + case 14: /* FS_VFAT_ */ + case 13: /* ACORN_ */ + case 15: /* MVS_ */ + case 18: /* THEOS_ */ + break; /* do nothing. */ + + default: /* assume the rest to be unix-like. */ + retval = 1; + break; + } /* switch */ + + return(retval); +} /* zip_version_does_symlinks */ + + +static int zip_entry_is_symlink(const ZIPentry *entry) +{ + return((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || + (entry->resolved == ZIP_BROKEN_SYMLINK) || + (entry->symlink)); +} /* zip_entry_is_symlink */ + + +static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr) +{ + PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF); + + return ( + (zip_version_does_symlinks(entry->version)) && + (entry->uncompressed_size > 0) && + ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) + ); +} /* zip_has_symlink_attr */ + + +static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime) +{ +#ifdef _WIN32_WCE + /* We have no struct tm and no mktime right now. + FIXME: This should probably be fixed at some point. + */ + return -1; +#else + PHYSFS_uint32 dosdate; + struct tm unixtime; + memset(&unixtime, '\0', sizeof (unixtime)); + + dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF); + dostime &= 0xFFFF; + + /* dissect date */ + unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; + unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; + unixtime.tm_mday = ((dosdate ) & 0x1F); + + /* dissect time */ + unixtime.tm_hour = ((dostime >> 11) & 0x1F); + unixtime.tm_min = ((dostime >> 5) & 0x3F); + unixtime.tm_sec = ((dostime << 1) & 0x3E); + + /* let mktime calculate daylight savings time. */ + unixtime.tm_isdst = -1; + + return((PHYSFS_sint64) mktime(&unixtime)); +#endif +} /* zip_dos_time_to_physfs_time */ + + +static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup) +{ + PHYSFS_uint16 fnamelen, extralen, commentlen; + PHYSFS_uint32 external_attr; + PHYSFS_uint16 ui16; + PHYSFS_uint32 ui32; + PHYSFS_sint64 si64; + + /* sanity check with central directory signature... */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0); + + /* Get the pertinent parts of the record... */ + BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits */ + BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); + BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */ + BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0); + entry->offset += ofs_fixup; + + entry->symlink = NULL; /* will be resolved later, if necessary. */ + entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? + ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; + + entry->name = (char *) allocator.Malloc(fnamelen + 1); + BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0); + if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1) + goto zip_load_entry_puked; + + entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ + zip_convert_dos_path(entry, entry->name); + + si64 = __PHYSFS_platformTell(in); + if (si64 == -1) + goto zip_load_entry_puked; + + /* seek to the start of the next entry in the central directory... */ + if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen)) + goto zip_load_entry_puked; + + return(1); /* success. */ + +zip_load_entry_puked: + allocator.Free(entry->name); + return(0); /* failure. */ +} /* zip_load_entry */ + + +static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const ZIPentry *a = (const ZIPentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* zip_entry_cmp */ + + +static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + ZIPentry tmp; + ZIPentry *first = &(((ZIPentry *) _a)[one]); + ZIPentry *second = &(((ZIPentry *) _a)[two]); + memcpy(&tmp, first, sizeof (ZIPentry)); + memcpy(first, second, sizeof (ZIPentry)); + memcpy(second, &tmp, sizeof (ZIPentry)); + } /* if */ +} /* zip_entry_swap */ + + +static int zip_load_entries(void *in, ZIPinfo *info, + PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs) +{ + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, central_ofs), NULL, 0); + + info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max); + BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0); + + for (i = 0; i < max; i++) + { + if (!zip_load_entry(in, &info->entries[i], data_ofs)) + { + zip_free_entries(info->entries, i); + return(0); + } /* if */ + } /* for */ + + __PHYSFS_sort(info->entries, max, zip_entry_cmp, zip_entry_swap); + return(1); +} /* zip_load_entries */ + + +static int zip_parse_end_of_central_dir(void *in, ZIPinfo *info, + PHYSFS_uint32 *data_start, + PHYSFS_uint32 *central_dir_ofs) +{ + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_sint64 len; + PHYSFS_sint64 pos; + + /* find the end-of-central-dir record, and seek to it. */ + pos = zip_find_end_of_central_dir(in, &len); + BAIL_IF_MACRO(pos == -1, NULL, 0); + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, pos), NULL, 0); + + /* check signature again, just in case. */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, ERR_NOT_AN_ARCHIVE, 0); + + /* number of this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* number of the disk with the start of the central directory */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* total number of entries in the central dir on this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* total number of entries in the central dir */ + BAIL_IF_MACRO(!readui16(in, &info->entryCount), NULL, 0); + BAIL_IF_MACRO(ui16 != info->entryCount, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* size of the central directory */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + + /* offset of central directory */ + BAIL_IF_MACRO(!readui32(in, central_dir_ofs), NULL, 0); + BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* + * For self-extracting archives, etc, there's crapola in the file + * before the zipfile records; we calculate how much data there is + * prepended by determining how far the central directory offset is + * from where it is supposed to be (start of end-of-central-dir minus + * sizeof central dir)...the difference in bytes is how much arbitrary + * data is at the start of the physical file. + */ + *data_start = (PHYSFS_uint32) (pos - (*central_dir_ofs + ui32)); + + /* Now that we know the difference, fix up the central dir offset... */ + *central_dir_ofs += *data_start; + + /* zipfile comment length */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* + * Make sure that the comment length matches to the end of file... + * If it doesn't, we're either in the wrong part of the file, or the + * file is corrupted, but we give up either way. + */ + BAIL_IF_MACRO((pos + 22 + ui16) != len, ERR_UNSUPPORTED_ARCHIVE, 0); + + return(1); /* made it. */ +} /* zip_parse_end_of_central_dir */ + + +static ZIPinfo *zip_create_zipinfo(const char *name) +{ + char *ptr; + ZIPinfo *info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + memset(info, '\0', sizeof (ZIPinfo)); + + ptr = (char *) allocator.Malloc(strlen(name) + 1); + if (ptr == NULL) + { + allocator.Free(info); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + info->archiveName = ptr; + strcpy(info->archiveName, name); + return(info); +} /* zip_create_zipinfo */ + + +static void *ZIP_openArchive(const char *name, int forWriting) +{ + void *in = NULL; + ZIPinfo *info = NULL; + PHYSFS_uint32 data_start; + PHYSFS_uint32 cent_dir_ofs; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + + if ((in = __PHYSFS_platformOpenRead(name)) == NULL) + goto zip_openarchive_failed; + + if ((info = zip_create_zipinfo(name)) == NULL) + goto zip_openarchive_failed; + + if (!zip_parse_end_of_central_dir(in, info, &data_start, ¢_dir_ofs)) + goto zip_openarchive_failed; + + if (!zip_load_entries(in, info, data_start, cent_dir_ofs)) + goto zip_openarchive_failed; + + __PHYSFS_platformClose(in); + return(info); + +zip_openarchive_failed: + if (info != NULL) + { + if (info->archiveName != NULL) + allocator.Free(info->archiveName); + allocator.Free(info); + } /* if */ + + if (in != NULL) + __PHYSFS_platformClose(in); + + return(NULL); +} /* ZIP_openArchive */ + + +static PHYSFS_sint32 zip_find_start_of_dir(ZIPinfo *info, const char *path, + int stop_on_first_find) +{ + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + PHYSFS_uint32 dlen = strlen(path); + PHYSFS_sint32 retval = -1; + const char *name; + int rc; + + if (*path == '\0') /* root dir? */ + return(0); + + if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + name = info->entries[middle].name; + rc = strncmp(path, name, dlen); + if (rc == 0) + { + char ch = name[dlen]; + if ('/' < ch) /* make sure this isn't just a substr match. */ + rc = -1; + else if ('/' > ch) + rc = 1; + else + { + if (stop_on_first_find) /* Just checking dir's existance? */ + return(middle); + + if (name[dlen + 1] == '\0') /* Skip initial dir entry. */ + return(middle + 1); + + /* there might be more entries earlier in the list. */ + retval = middle; + hi = middle - 1; + } /* else */ + } /* if */ + + if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + return(retval); +} /* zip_find_start_of_dir */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, PHYSFS_sint32 ln) +{ + char *newstr = __PHYSFS_smallAlloc(ln + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, ln); + newstr[ln] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void ZIP_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + ZIPinfo *info = ((ZIPinfo *) opaque); + PHYSFS_sint32 dlen, dlen_inc, max, i; + + i = zip_find_start_of_dir(info, dname, 0); + if (i == -1) /* no such directory. */ + return; + + dlen = strlen(dname); + if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; + max = (PHYSFS_sint32) info->entryCount; + while (i < max) + { + char *e = info->entries[i].name; + if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/'))) + break; /* past end of this dir; we're done. */ + + if ((omitSymLinks) && (zip_entry_is_symlink(&info->entries[i]))) + i++; + else + { + char *add = e + dlen_inc; + char *ptr = strchr(add, '/'); + PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add)); + doEnumCallback(cb, callbackdata, origdir, add, ln); + ln += dlen_inc; /* point past entry to children... */ + + /* increment counter and skip children of subdirs... */ + while ((++i < max) && (ptr != NULL)) + { + char *e_new = info->entries[i].name; + if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/')) + break; + } /* while */ + } /* else */ + } /* while */ +} /* ZIP_enumerateFiles */ + + +static int ZIP_exists(dvoid *opaque, const char *name) +{ + int isDir; + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + return((entry != NULL) || (isDir)); +} /* ZIP_exists */ + + +static PHYSFS_sint64 ZIP_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + int isDir; + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* Best I can do for a dir... */ + + BAIL_IF_MACRO(entry == NULL, NULL, -1); + return(entry->last_mod_time); +} /* ZIP_getLastModTime */ + + +static int ZIP_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + ZIPinfo *info = (ZIPinfo *) opaque; + int isDir; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* definitely a dir. */ + + /* Follow symlinks. This means we might need to resolve entries. */ + BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, 0); + + if (entry->resolved == ZIP_UNRESOLVED_SYMLINK) /* gotta resolve it. */ + { + int rc; + void *in = __PHYSFS_platformOpenRead(info->archiveName); + BAIL_IF_MACRO(in == NULL, NULL, 0); + rc = zip_resolve(in, info, entry); + __PHYSFS_platformClose(in); + if (!rc) + return(0); + } /* if */ + + BAIL_IF_MACRO(entry->resolved == ZIP_BROKEN_SYMLINK, NULL, 0); + BAIL_IF_MACRO(entry->symlink == NULL, ERR_NOT_A_DIR, 0); + + return(zip_find_start_of_dir(info, entry->symlink->name, 1) >= 0); +} /* ZIP_isDirectory */ + + +static int ZIP_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + int isDir; + const ZIPentry *entry = zip_find_entry((ZIPinfo *) opaque, name, &isDir); + *fileExists = ((isDir) || (entry != NULL)); + BAIL_IF_MACRO(entry == NULL, NULL, 0); + return(zip_entry_is_symlink(entry)); +} /* ZIP_isSymLink */ + + +static void *zip_get_file_handle(const char *fn, ZIPinfo *inf, ZIPentry *entry) +{ + int success; + void *retval = __PHYSFS_platformOpenRead(fn); + BAIL_IF_MACRO(retval == NULL, NULL, NULL); + + success = zip_resolve(retval, inf, entry); + if (success) + { + PHYSFS_sint64 offset; + offset = ((entry->symlink) ? entry->symlink->offset : entry->offset); + success = __PHYSFS_platformSeek(retval, offset); + } /* if */ + + if (!success) + { + __PHYSFS_platformClose(retval); + retval = NULL; + } /* if */ + + return(retval); +} /* zip_get_file_handle */ + + +static fvoid *ZIP_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, fnm, NULL); + ZIPfileinfo *finfo = NULL; + void *in; + + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + in = zip_get_file_handle(info->archiveName, info, entry); + BAIL_IF_MACRO(in == NULL, NULL, NULL); + + finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); + if (finfo == NULL) + { + __PHYSFS_platformClose(in); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + memset(finfo, '\0', sizeof (ZIPfileinfo)); + finfo->handle = in; + finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); + initializeZStream(&finfo->stream); + if (finfo->entry->compression_method != COMPMETH_NONE) + { + if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK) + { + ZIP_fileClose(finfo); + return(NULL); + } /* if */ + + finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE); + if (finfo->buffer == NULL) + { + ZIP_fileClose(finfo); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + } /* if */ + + return(finfo); +} /* ZIP_openRead */ + + +static fvoid *ZIP_openWrite(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* ZIP_openWrite */ + + +static fvoid *ZIP_openAppend(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* ZIP_openAppend */ + + +static void ZIP_dirClose(dvoid *opaque) +{ + ZIPinfo *zi = (ZIPinfo *) (opaque); + zip_free_entries(zi->entries, zi->entryCount); + allocator.Free(zi->archiveName); + allocator.Free(zi); +} /* ZIP_dirClose */ + + +static int ZIP_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* ZIP_remove */ + + +static int ZIP_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* ZIP_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP = +{ + "ZIP", + ZIP_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_ZIP = +{ + &__PHYSFS_ArchiveInfo_ZIP, + ZIP_isArchive, /* isArchive() method */ + ZIP_openArchive, /* openArchive() method */ + ZIP_enumerateFiles, /* enumerateFiles() method */ + ZIP_exists, /* exists() method */ + ZIP_isDirectory, /* isDirectory() method */ + ZIP_isSymLink, /* isSymLink() method */ + ZIP_getLastModTime, /* getLastModTime() method */ + ZIP_openRead, /* openRead() method */ + ZIP_openWrite, /* openWrite() method */ + ZIP_openAppend, /* openAppend() method */ + ZIP_remove, /* remove() method */ + ZIP_mkdir, /* mkdir() method */ + ZIP_dirClose, /* dirClose() method */ + ZIP_read, /* read() method */ + ZIP_write, /* write() method */ + ZIP_eof, /* eof() method */ + ZIP_tell, /* tell() method */ + ZIP_seek, /* seek() method */ + ZIP_fileLength, /* fileLength() method */ + ZIP_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_ZIP */ + +/* end of zip.c ... */ + diff --git a/3rdparty/physfs/lzma/7zC.txt b/3rdparty/physfs/lzma/7zC.txt new file mode 100644 index 0000000..8412954 --- /dev/null +++ b/3rdparty/physfs/lzma/7zC.txt @@ -0,0 +1,237 @@ +7z ANSI-C Decoder 4.48 +---------------------- + +7z ANSI-C Decoder 4.48 Copyright (C) 1999-2006 Igor Pavlov + +7z ANSI-C provides 7z/LZMA decoding. +7z ANSI-C version is simplified version ported from C++ code. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + + +LICENSE +------- + +Read lzma.txt for information about license. + + +Files +--------------------- + +7zAlloc.* - Allocate and Free +7zBuffer.* - Buffer structure +7zCrc.* - CRC32 code +7zDecode.* - Low level memory->memory decoding +7zExtract.* - High level stream->memory decoding +7zHeader.* - .7z format constants +7zIn.* - .7z archive opening +7zItem.* - .7z structures +7zMain.c - Test application +7zMethodID.* - MethodID structure +7zTypes.h - Base types and constants + + +How To Use +---------- + +You must download 7-Zip program from www.7-zip.org. + +You can create .7z archive with 7z.exe or 7za.exe: + + 7za.exe a archive.7z *.htm -r -mx -m0fb=255 + +If you have big number of files in archive, and you need fast extracting, +you can use partly-solid archives: + + 7za.exe a archive.7z *.htm -ms=512K -r -mx -m0fb=255 -m0d=512K + +In that example 7-Zip will use 512KB solid blocks. So it needs to decompress only +512KB for extracting one file from such archive. + + +Limitations of current version of 7z ANSI-C Decoder +--------------------------------------------------- + + - It reads only "FileName", "Size", "LastWriteTime" and "CRC" information for each file in archive. + - It supports only LZMA and Copy (no compression) methods with BCJ or BCJ2 filters. + - It converts original UTF-16 Unicode file names to UTF-8 Unicode file names. + +These limitations will be fixed in future versions. + + +Using 7z ANSI-C Decoder Test application: +----------------------------------------- + +Usage: 7zDec + +: + e: Extract files from archive + l: List contents of archive + t: Test integrity of archive + +Example: + + 7zDec l archive.7z + +lists contents of archive.7z + + 7zDec e archive.7z + +extracts files from archive.7z to current folder. + + +How to use .7z Decoder +---------------------- + +.7z Decoder can be compiled in one of two modes: + +1) Default mode. In that mode 7z Decoder will read full compressed + block to RAM before decompressing. + +2) Mode with defined _LZMA_IN_CB. In that mode 7z Decoder can read + compressed block by parts. And you can specify desired buffer size. + So memory requirements can be reduced. But decompressing speed will + be 5-10% lower and code size is slightly larger. + + +Memory allocation +~~~~~~~~~~~~~~~~~ + +7z Decoder uses two memory pools: +1) Temporary pool +2) Main pool +Such scheme can allow you to avoid fragmentation of allocated blocks. + +Steps for using 7z decoder +-------------------------- + +Use code at 7zMain.c as example. + +1) Declare variables: + inStream /* implements ISzInStream interface */ + CArchiveDatabaseEx db; /* 7z archive database structure */ + ISzAlloc allocImp; /* memory functions for main pool */ + ISzAlloc allocTempImp; /* memory functions for temporary pool */ + +2) call InitCrcTable(); function to initialize CRC structures. + +3) call SzArDbExInit(&db); function to initialize db structures. + +4) call SzArchiveOpen(inStream, &db, &allocMain, &allocTemp) to open archive + +This function opens archive "inStream" and reads headers to "db". +All items in "db" will be allocated with "allocMain" functions. +SzArchiveOpen function allocates and frees temporary structures by "allocTemp" functions. + +5) List items or Extract items + + Listing code: + ~~~~~~~~~~~~~ + { + UInt32 i; + for (i = 0; i < db.Database.NumFiles; i++) + { + CFileItem *f = db.Database.Files + i; + printf("%10d %s\n", (int)f->Size, f->Name); + } + } + + Extracting code: + ~~~~~~~~~~~~~~~~ + + SZ_RESULT SzExtract( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + If you need to decompress more than one file, you can send these values from previous call: + blockIndex, + outBuffer, + outBufferSize, + You can consider "outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + After decompressing you must free "outBuffer": + allocImp.Free(outBuffer); + +6) call SzArDbExFree(&db, allocImp.Free) to free allocated items in "db". + + + + +Memory requirements for .7z decoding +------------------------------------ + +Memory usage for Archive opening: + - Temporary pool: + - Memory for compressed .7z headers (if _LZMA_IN_CB is not defined) + - Memory for uncompressed .7z headers + - some other temporary blocks + - Main pool: + - Memory for database: + Estimated size of one file structures in solid archive: + - Size (4 or 8 Bytes) + - CRC32 (4 bytes) + - LastWriteTime (8 bytes) + - Some file information (4 bytes) + - File Name (variable length) + pointer + allocation structures + +Memory usage for archive Decompressing: + - Temporary pool: + - Memory for compressed solid block (if _LZMA_IN_CB is not defined) + - Memory for LZMA decompressing structures + - Main pool: + - Memory for decompressed solid block + - Memory for temprorary buffers, if BCJ2 fileter is used. Usually these + temprorary buffers can be about 15% of solid block size. + + +If _LZMA_IN_CB is defined, 7z Decoder will not allocate memory for +compressed blocks. Instead of this, you must allocate buffer with desired +size before calling 7z Decoder. Use 7zMain.c as example. + + + +EXIT codes +----------- + +7z Decoder functions can return one of the following codes: + +#define SZ_OK (0) +#define SZE_DATA_ERROR (1) +#define SZE_OUTOFMEMORY (2) +#define SZE_CRC_ERROR (3) + +#define SZE_NOTIMPL (4) +#define SZE_FAIL (5) + +#define SZE_ARCHIVE_ERROR (6) + + + +LZMA Defines +------------ + +_LZMA_IN_CB - Use special callback mode for input stream to reduce memory requirements + +_SZ_FILE_SIZE_32 - define it if you need only support for files smaller than 4 GB +_SZ_NO_INT_64 - define it if your compiler doesn't support long long int or __int64. + +_LZMA_PROB32 - it can increase LZMA decompressing speed on some 32-bit CPUs. + +_SZ_ALLOC_DEBUG - define it if you want to debug alloc/free operations to stderr. + + +--- + +http://www.7-zip.org +http://www.7-zip.org/support.html diff --git a/3rdparty/physfs/lzma/7zFormat.txt b/3rdparty/physfs/lzma/7zFormat.txt new file mode 100644 index 0000000..e1cf738 --- /dev/null +++ b/3rdparty/physfs/lzma/7zFormat.txt @@ -0,0 +1,471 @@ +7z Format description (2.30 Beta 25) +----------------------------------- + +This file contains description of 7z archive format. +7z archive can contain files compressed with any method. +See "Methods.txt" for description for defined compressing methods. + + +Format structure Overview +------------------------- + +Some fields can be optional. + +Archive structure +~~~~~~~~~~~~~~~~~ +SignatureHeader +[PackedStreams] +[PackedStreamsForHeaders] +[ + Header + or + { + Packed Header + HeaderInfo + } +] + + + +Header structure +~~~~~~~~~~~~~~~~ +{ + ArchiveProperties + AdditionalStreams + { + PackInfo + { + PackPos + NumPackStreams + Sizes[NumPackStreams] + CRCs[NumPackStreams] + } + CodersInfo + { + NumFolders + Folders[NumFolders] + { + NumCoders + CodersInfo[NumCoders] + { + ID + NumInStreams; + NumOutStreams; + PropertiesSize + Properties[PropertiesSize] + } + NumBindPairs + BindPairsInfo[NumBindPairs] + { + InIndex; + OutIndex; + } + PackedIndices + } + UnPackSize[Folders][Folders.NumOutstreams] + CRCs[NumFolders] + } + SubStreamsInfo + { + NumUnPackStreamsInFolders[NumFolders]; + UnPackSizes[] + CRCs[] + } + } + MainStreamsInfo + { + (Same as in AdditionalStreams) + } + FilesInfo + { + NumFiles + Properties[] + { + ID + Size + Data + } + } +} + +HeaderInfo structure +~~~~~~~~~~~~~~~~~~~~ +{ + (Same as in AdditionalStreams) +} + + + +Notes about Notation and encoding +--------------------------------- + +7z uses little endian encoding. + +7z archive format has optional headers that are marked as +[] +Header +[] + +REAL_UINT64 means real UINT64. + +UINT64 means real UINT64 encoded with the following scheme: + + Size of encoding sequence depends from first byte: + First_Byte Extra_Bytes Value + (binary) + 0xxxxxxx : ( xxxxxxx ) + 10xxxxxx BYTE y[1] : ( xxxxxx << (8 * 1)) + y + 110xxxxx BYTE y[2] : ( xxxxx << (8 * 2)) + y + ... + 1111110x BYTE y[6] : ( x << (8 * 6)) + y + 11111110 BYTE y[7] : y + 11111111 BYTE y[8] : y + + + +Property IDs +------------ + +0x00 = kEnd, + +0x01 = kHeader, + +0x02 = kArchiveProperties, + +0x03 = kAdditionalStreamsInfo, +0x04 = kMainStreamsInfo, +0x05 = kFilesInfo, + +0x06 = kPackInfo, +0x07 = kUnPackInfo, +0x08 = kSubStreamsInfo, + +0x09 = kSize, +0x0A = kCRC, + +0x0B = kFolder, + +0x0C = kCodersUnPackSize, +0x0D = kNumUnPackStream, + +0x0E = kEmptyStream, +0x0F = kEmptyFile, +0x10 = kAnti, + +0x11 = kName, +0x12 = kCreationTime, +0x13 = kLastAccessTime, +0x14 = kLastWriteTime, +0x15 = kWinAttributes, +0x16 = kComment, + +0x17 = kEncodedHeader, + + +7z format headers +----------------- + +SignatureHeader +~~~~~~~~~~~~~~~ + BYTE kSignature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + + ArchiveVersion + { + BYTE Major; // now = 0 + BYTE Minor; // now = 2 + }; + + UINT32 StartHeaderCRC; + + StartHeader + { + REAL_UINT64 NextHeaderOffset + REAL_UINT64 NextHeaderSize + UINT32 NextHeaderCRC + } + + +........................... + + +ArchiveProperties +~~~~~~~~~~~~~~~~~ +BYTE NID::kArchiveProperties (0x02) +for (;;) +{ + BYTE PropertyType; + if (aType == 0) + break; + UINT64 PropertySize; + BYTE PropertyData[PropertySize]; +} + + +Digests (NumStreams) +~~~~~~~~~~~~~~~~~~~~~ + BYTE AllAreDefined + if (AllAreDefined == 0) + { + for(NumStreams) + BIT Defined + } + UINT32 CRCs[NumDefined] + + +PackInfo +~~~~~~~~~~~~ + BYTE NID::kPackInfo (0x06) + UINT64 PackPos + UINT64 NumPackStreams + + [] + BYTE NID::kSize (0x09) + UINT64 PackSizes[NumPackStreams] + [] + + [] + BYTE NID::kCRC (0x0A) + PackStreamDigests[NumPackStreams] + [] + + BYTE NID::kEnd + + +Folder +~~~~~~ + UINT64 NumCoders; + for (NumCoders) + { + BYTE + { + 0:3 DecompressionMethod.IDSize + 4: + 0 - IsSimple + 1 - Is not simple + 5: + 0 - No Attributes + 1 - There Are Attributes + 7: + 0 - Last Method in Alternative_Method_List + 1 - There are more alternative methods + } + BYTE DecompressionMethod.ID[DecompressionMethod.IDSize] + if (!IsSimple) + { + UINT64 NumInStreams; + UINT64 NumOutStreams; + } + if (DecompressionMethod[0] != 0) + { + UINT64 PropertiesSize + BYTE Properties[PropertiesSize] + } + } + + NumBindPairs = NumOutStreamsTotal - 1; + + for (NumBindPairs) + { + UINT64 InIndex; + UINT64 OutIndex; + } + + NumPackedStreams = NumInStreamsTotal - NumBindPairs; + if (NumPackedStreams > 1) + for(NumPackedStreams) + { + UINT64 Index; + }; + + + + +Coders Info +~~~~~~~~~~~ + + BYTE NID::kUnPackInfo (0x07) + + + BYTE NID::kFolder (0x0B) + UINT64 NumFolders + BYTE External + switch(External) + { + case 0: + Folders[NumFolders] + case 1: + UINT64 DataStreamIndex + } + + + BYTE ID::kCodersUnPackSize (0x0C) + for(Folders) + for(Folder.NumOutStreams) + UINT64 UnPackSize; + + + [] + BYTE NID::kCRC (0x0A) + UnPackDigests[NumFolders] + [] + + + + BYTE NID::kEnd + + + +SubStreams Info +~~~~~~~~~~~~~~ + BYTE NID::kSubStreamsInfo; (0x08) + + [] + BYTE NID::kNumUnPackStream; (0x0D) + UINT64 NumUnPackStreamsInFolders[NumFolders]; + [] + + + [] + BYTE NID::kSize (0x09) + UINT64 UnPackSizes[] + [] + + + [] + BYTE NID::kCRC (0x0A) + Digests[Number of streams with unknown CRC] + [] + + + BYTE NID::kEnd + + +Streams Info +~~~~~~~~~~~~ + + [] + PackInfo + [] + + + [] + CodersInfo + [] + + + [] + SubStreamsInfo + [] + + BYTE NID::kEnd + + +FilesInfo +~~~~~~~~~ + BYTE NID::kFilesInfo; (0x05) + UINT64 NumFiles + + for (;;) + { + BYTE PropertyType; + if (aType == 0) + break; + + UINT64 Size; + + switch(PropertyType) + { + kEmptyStream: (0x0E) + for(NumFiles) + BIT IsEmptyStream + + kEmptyFile: (0x0F) + for(EmptyStreams) + BIT IsEmptyFile + + kAnti: (0x10) + for(EmptyStreams) + BIT IsAntiFile + + case kCreationTime: (0x12) + case kLastAccessTime: (0x13) + case kLastWriteTime: (0x14) + BYTE AllAreDefined + if (AllAreDefined == 0) + { + for(NumFiles) + BIT TimeDefined + } + BYTE External; + if(External != 0) + UINT64 DataIndex + [] + for(Definded Items) + UINT32 Time + [] + + kNames: (0x11) + BYTE External; + if(External != 0) + UINT64 DataIndex + [] + for(Files) + { + wchar_t Names[NameSize]; + wchar_t 0; + } + [] + + kAttributes: (0x15) + BYTE AllAreDefined + if (AllAreDefined == 0) + { + for(NumFiles) + BIT AttributesAreDefined + } + BYTE External; + if(External != 0) + UINT64 DataIndex + [] + for(Definded Attributes) + UINT32 Attributes + [] + } + } + + +Header +~~~~~~ + BYTE NID::kHeader (0x01) + + [] + ArchiveProperties + [] + + [] + BYTE NID::kAdditionalStreamsInfo; (0x03) + StreamsInfo + [] + + [] + BYTE NID::kMainStreamsInfo; (0x04) + StreamsInfo + [] + + [] + FilesInfo + [] + + BYTE NID::kEnd + + +HeaderInfo +~~~~~~~~~~ + [] + BYTE NID::kEncodedHeader; (0x17) + StreamsInfo for Encoded Header + [] + + +--- +End of document diff --git a/3rdparty/physfs/lzma/C/7zCrc.c b/3rdparty/physfs/lzma/C/7zCrc.c new file mode 100644 index 0000000..1436b99 --- /dev/null +++ b/3rdparty/physfs/lzma/C/7zCrc.c @@ -0,0 +1,32 @@ +/* 7zCrc.c */ + +#include "7zCrc.h" + +#define kCrcPoly 0xEDB88320 +UInt32 g_CrcTable[256]; + +void MY_FAST_CALL CrcGenerateTable(void) +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } +} + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 ; size--, p++) + v = CRC_UPDATE_BYTE(v, *p); + return v; +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return CrcUpdate(CRC_INIT_VAL, data, size) ^ 0xFFFFFFFF; +} diff --git a/3rdparty/physfs/lzma/C/7zCrc.h b/3rdparty/physfs/lzma/C/7zCrc.h new file mode 100644 index 0000000..6cb1016 --- /dev/null +++ b/3rdparty/physfs/lzma/C/7zCrc.h @@ -0,0 +1,21 @@ +/* 7zCrc.h */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include + +#include "Types.h" + +extern UInt32 g_CrcTable[]; + +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ 0xFFFFFFFF) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +#endif diff --git a/3rdparty/physfs/lzma/C/7zCrcT8.c b/3rdparty/physfs/lzma/C/7zCrcT8.c new file mode 100644 index 0000000..83aa95f --- /dev/null +++ b/3rdparty/physfs/lzma/C/7zCrcT8.c @@ -0,0 +1,40 @@ +/* 7zCrcT8.c */ + +#include "7zCrc.h" + +#define kCrcPoly 0xEDB88320 +#define CRC_NUM_TABLES 8 + +UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; + +void MY_FAST_CALL CrcGenerateTable() +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } + #if CRC_NUM_TABLES > 1 + for (; i < 256 * CRC_NUM_TABLES; i++) + { + UInt32 r = g_CrcTable[i - 256]; + g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); + } + #endif +} + +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + return CrcUpdateT8(v, data, size, g_CrcTable); +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return CrcUpdateT8(CRC_INIT_VAL, data, size, g_CrcTable) ^ 0xFFFFFFFF; +} diff --git a/3rdparty/physfs/lzma/C/Alloc.c b/3rdparty/physfs/lzma/C/Alloc.c new file mode 100644 index 0000000..7b5af42 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Alloc.c @@ -0,0 +1,119 @@ +/* Alloc.c */ + +#ifdef _WIN32 +#include +#endif +#include + +#include "Alloc.h" + +/* #define _SZ_ALLOC_DEBUG */ + +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ +#ifdef _SZ_ALLOC_DEBUG +#include +int g_allocCount = 0; +int g_allocCountMid = 0; +int g_allocCountBig = 0; +#endif + +void *MyAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount++); + #endif + return malloc(size); +} + +void MyFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree; count = %10d", --g_allocCount); + #endif + free(address); +} + +#ifdef _WIN32 + +void *MidAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void MidFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); + #endif + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#ifndef MEM_LARGE_PAGES +#undef _7ZIP_LARGE_PAGES +#endif + +#ifdef _7ZIP_LARGE_PAGES +SIZE_T g_LargePageSize = 0; +typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); +#endif + +void SetLargePageSize() +{ + #ifdef _7ZIP_LARGE_PAGES + SIZE_T size = 0; + GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); + if (largePageMinimum == 0) + return; + size = largePageMinimum(); + if (size == 0 || (size & (size - 1)) != 0) + return; + g_LargePageSize = size; + #endif +} + + +void *BigAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); + #endif + + #ifdef _7ZIP_LARGE_PAGES + if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) + { + void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), + MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + if (res != 0) + return res; + } + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void BigFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); + #endif + + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#endif diff --git a/3rdparty/physfs/lzma/C/Alloc.h b/3rdparty/physfs/lzma/C/Alloc.h new file mode 100644 index 0000000..d748cb1 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Alloc.h @@ -0,0 +1,29 @@ +/* Alloc.h */ + +#ifndef __COMMON_ALLOC_H +#define __COMMON_ALLOC_H + +#include + +void *MyAlloc(size_t size); +void MyFree(void *address); + +#ifdef _WIN32 + +void SetLargePageSize(); + +void *MidAlloc(size_t size); +void MidFree(void *address); +void *BigAlloc(size_t size); +void BigFree(void *address); + +#else + +#define MidAlloc(size) MyAlloc(size) +#define MidFree(address) MyFree(address) +#define BigAlloc(size) MyAlloc(size) +#define BigFree(address) MyFree(address) + +#endif + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.c b/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.c new file mode 100644 index 0000000..21bb30c --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.c @@ -0,0 +1,70 @@ +/* 7zAlloc.c */ + +#include +#include "7zAlloc.h" + +/* #define _SZ_ALLOC_DEBUG */ +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ + +#ifdef _SZ_ALLOC_DEBUG + +#ifdef _WIN32 +#include +#endif +#include +int g_allocCount = 0; +int g_allocCountTemp = 0; +#endif + +void *SzAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount); + g_allocCount++; + #endif + return malloc(size); +} + +void SzFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCount--; + fprintf(stderr, "\nFree; count = %10d", g_allocCount); + } + #endif + free(address); +} + +void *SzAllocTemp(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_temp %10d bytes; count = %10d", size, g_allocCountTemp); + g_allocCountTemp++; + #ifdef _WIN32 + return HeapAlloc(GetProcessHeap(), 0, size); + #endif + #endif + return malloc(size); +} + +void SzFreeTemp(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCountTemp--; + fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp); + } + #ifdef _WIN32 + HeapFree(GetProcessHeap(), 0, address); + return; + #endif + #endif + free(address); +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.h b/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.h new file mode 100644 index 0000000..4ca4170 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zAlloc.h @@ -0,0 +1,20 @@ +/* 7zAlloc.h */ + +#ifndef __7Z_ALLOC_H +#define __7Z_ALLOC_H + +#include + +typedef struct _ISzAlloc +{ + void *(*Alloc)(size_t size); + void (*Free)(void *address); /* address can be 0 */ +} ISzAlloc; + +void *SzAlloc(size_t size); +void SzFree(void *address); + +void *SzAllocTemp(size_t size); +void SzFreeTemp(void *address); + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.c b/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.c new file mode 100644 index 0000000..3c4b71e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.c @@ -0,0 +1,29 @@ +/* 7zBuffer.c */ + +#include "7zBuffer.h" +#include "7zAlloc.h" + +void SzByteBufferInit(CSzByteBuffer *buffer) +{ + buffer->Capacity = 0; + buffer->Items = 0; +} + +int SzByteBufferCreate(CSzByteBuffer *buffer, size_t newCapacity, void * (*allocFunc)(size_t size)) +{ + buffer->Capacity = newCapacity; + if (newCapacity == 0) + { + buffer->Items = 0; + return 1; + } + buffer->Items = (Byte *)allocFunc(newCapacity); + return (buffer->Items != 0); +} + +void SzByteBufferFree(CSzByteBuffer *buffer, void (*freeFunc)(void *)) +{ + freeFunc(buffer->Items); + buffer->Items = 0; + buffer->Capacity = 0; +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.h b/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.h new file mode 100644 index 0000000..05c6d74 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zBuffer.h @@ -0,0 +1,19 @@ +/* 7zBuffer.h */ + +#ifndef __7Z_BUFFER_H +#define __7Z_BUFFER_H + +#include +#include "../../Types.h" + +typedef struct _CSzByteBuffer +{ + size_t Capacity; + Byte *Items; +}CSzByteBuffer; + +void SzByteBufferInit(CSzByteBuffer *buffer); +int SzByteBufferCreate(CSzByteBuffer *buffer, size_t newCapacity, void * (*allocFunc)(size_t size)); +void SzByteBufferFree(CSzByteBuffer *buffer, void (*freeFunc)(void *)); + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.c b/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.c new file mode 100644 index 0000000..524e547 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.c @@ -0,0 +1,345 @@ +/* 7zDecode.c */ + +#include + +/* BEGIN PHYSFS CHANGE */ +#include +/* END PHYSFS CHANGE */ + +#include "7zDecode.h" +#ifdef _SZ_ONE_DIRECTORY +#include "LzmaDecode.h" +#else +#include "../../Compress/Lzma/LzmaDecode.h" +#include "../../Compress/Branch/BranchX86.h" +#include "../../Compress/Branch/BranchX86_2.h" +#endif + +#define k_Copy 0 +#define k_LZMA 0x30101 +#define k_BCJ 0x03030103 +#define k_BCJ2 0x0303011B + +#ifdef _LZMA_IN_CB + +typedef struct _CLzmaInCallbackImp +{ + ILzmaInCallback InCallback; + ISzInStream *InStream; + CFileSize Size; +} CLzmaInCallbackImp; + +int LzmaReadImp(void *object, const unsigned char **buffer, SizeT *size) +{ + CLzmaInCallbackImp *cb = (CLzmaInCallbackImp *)object; + size_t processedSize; + SZ_RESULT res; + size_t curSize = (1 << 20); + if (curSize > cb->Size) + curSize = (size_t)cb->Size; + *size = 0; + res = cb->InStream->Read((void *)cb->InStream, (void **)buffer, curSize, &processedSize); + *size = (SizeT)processedSize; + if (processedSize > curSize) + return (int)SZE_FAIL; + cb->Size -= processedSize; + if (res == SZ_OK) + return 0; + return (int)res; +} + +#endif + +SZ_RESULT SzDecodeLzma(CCoderInfo *coder, CFileSize inSize, + #ifdef _LZMA_IN_CB + ISzInStream *inStream, + #else + const Byte *inBuffer, + #endif + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + #ifdef _LZMA_IN_CB + CLzmaInCallbackImp lzmaCallback; + #else + SizeT inProcessed; + #endif + + CLzmaDecoderState state; /* it's about 24-80 bytes structure, if int is 32-bit */ + int result; + SizeT outSizeProcessedLoc; + + #ifdef _LZMA_IN_CB + lzmaCallback.Size = inSize; + lzmaCallback.InStream = inStream; + lzmaCallback.InCallback.Read = LzmaReadImp; + #endif + + if (LzmaDecodeProperties(&state.Properties, coder->Properties.Items, + (unsigned)coder->Properties.Capacity) != LZMA_RESULT_OK) + return SZE_FAIL; + + state.Probs = (CProb *)allocMain->Alloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (state.Probs == 0) + return SZE_OUTOFMEMORY; + + #ifdef _LZMA_OUT_READ + if (state.Properties.DictionarySize == 0) + state.Dictionary = 0; + else + { + state.Dictionary = (unsigned char *)allocMain->Alloc(state.Properties.DictionarySize); + if (state.Dictionary == 0) + { + allocMain->Free(state.Probs); + return SZE_OUTOFMEMORY; + } + } + LzmaDecoderInit(&state); + #endif + + result = LzmaDecode(&state, + #ifdef _LZMA_IN_CB + &lzmaCallback.InCallback, + #else + inBuffer, (SizeT)inSize, &inProcessed, + #endif + outBuffer, (SizeT)outSize, &outSizeProcessedLoc); + allocMain->Free(state.Probs); + #ifdef _LZMA_OUT_READ + allocMain->Free(state.Dictionary); + #endif + if (result == LZMA_RESULT_DATA_ERROR) + return SZE_DATA_ERROR; + if (result != LZMA_RESULT_OK) + return SZE_FAIL; + return (outSizeProcessedLoc == outSize) ? SZ_OK : SZE_DATA_ERROR; +} + +#ifdef _LZMA_IN_CB +SZ_RESULT SzDecodeCopy(CFileSize inSize, ISzInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + void *inBuffer; + size_t processedSize, curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)(inSize); + RINOK(inStream->Read((void *)inStream, (void **)&inBuffer, curSize, &processedSize)); + if (processedSize == 0) + return SZE_DATA_ERROR; + if (processedSize > curSize) + return SZE_FAIL; + memcpy(outBuffer, inBuffer, processedSize); + outBuffer += processedSize; + inSize -= processedSize; + } + return SZ_OK; +} +#endif + +#define IS_UNSUPPORTED_METHOD(m) ((m) != k_Copy && (m) != k_LZMA) +#define IS_UNSUPPORTED_CODER(c) (IS_UNSUPPORTED_METHOD(c.MethodID) || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ(c) (c.MethodID != k_BCJ || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ2(c) (c.MethodID != k_BCJ2 || c.NumInStreams != 4 || c.NumOutStreams != 1) + +SZ_RESULT CheckSupportedFolder(const CFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZE_NOTIMPL; + if (IS_UNSUPPORTED_CODER(f->Coders[0])) + return SZE_NOTIMPL; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + return SZE_NOTIMPL; + return SZ_OK; + } + if (f->NumCoders == 2) + { + if (IS_NO_BCJ(f->Coders[1]) || + f->NumPackStreams != 1 || f->PackStreams[0] != 0 || + f->NumBindPairs != 1 || + f->BindPairs[0].InIndex != 1 || f->BindPairs[0].OutIndex != 0) + return SZE_NOTIMPL; + return SZ_OK; + } + if (f->NumCoders == 4) + { + if (IS_UNSUPPORTED_CODER(f->Coders[1]) || + IS_UNSUPPORTED_CODER(f->Coders[2]) || + IS_NO_BCJ2(f->Coders[3])) + return SZE_NOTIMPL; + if (f->NumPackStreams != 4 || + f->PackStreams[0] != 2 || + f->PackStreams[1] != 6 || + f->PackStreams[2] != 1 || + f->PackStreams[3] != 0 || + f->NumBindPairs != 3 || + f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || + f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || + f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + return SZE_NOTIMPL; + return SZ_OK; + } + return SZE_NOTIMPL; +} + +CFileSize GetSum(const CFileSize *values, UInt32 index) +{ + CFileSize sum = 0; + UInt32 i; + for (i = 0; i < index; i++) + sum += values[i]; + return sum; +} + +SZ_RESULT SzDecode2(const CFileSize *packSizes, const CFolder *folder, + #ifdef _LZMA_IN_CB + ISzInStream *inStream, CFileSize startPos, + #else + const Byte *inBuffer, + #endif + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + size_t tempSizes[3] = { 0, 0, 0}; + size_t tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + CCoderInfo *coder = &folder->Coders[ci]; + + if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA) + { + UInt32 si = 0; + CFileSize offset; + CFileSize inSize; + Byte *outBufCur = outBuffer; + size_t outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + CFileSize unpackSize = folder->UnPackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (size_t)unpackSize; + if (outSizeCur != unpackSize) + return SZE_OUTOFMEMORY; + temp = (Byte *)allocMain->Alloc(outSizeCur); + if (temp == 0 && outSizeCur != 0) + return SZE_OUTOFMEMORY; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) + return SZE_OUTOFMEMORY; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (size_t)unpackSize; + } + else + return SZE_NOTIMPL; + } + offset = GetSum(packSizes, si); + inSize = packSizes[si]; + #ifdef _LZMA_IN_CB + RINOK(inStream->Seek(inStream, startPos + offset)); + #endif + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) + return SZE_DATA_ERROR; + + #ifdef _LZMA_IN_CB + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + #else + memcpy(outBufCur, inBuffer + (size_t)offset, (size_t)inSize); + #endif + } + else + { + SZ_RESULT res = SzDecodeLzma(coder, inSize, + #ifdef _LZMA_IN_CB + inStream, + #else + inBuffer + (size_t)offset, + #endif + outBufCur, outSizeCur, allocMain); + RINOK(res) + } + } + else if (coder->MethodID == k_BCJ) + { + UInt32 state; + if (ci != 1) + return SZE_NOTIMPL; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + } + else if (coder->MethodID == k_BCJ2) + { + CFileSize offset = GetSum(packSizes, 1); + CFileSize s3Size = packSizes[1]; + SZ_RESULT res; + if (ci != 3) + return SZE_NOTIMPL; + + #ifdef _LZMA_IN_CB + RINOK(inStream->Seek(inStream, startPos + offset)); + tempSizes[2] = (size_t)s3Size; + if (tempSizes[2] != s3Size) + return SZE_OUTOFMEMORY; + tempBuf[2] = (Byte *)allocMain->Alloc(tempSizes[2]); + if (tempBuf[2] == 0 && tempSizes[2] != 0) + return SZE_OUTOFMEMORY; + res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); + RINOK(res) + #endif + + res = x86_2_Decode( + tempBuf3, tempSize3, + tempBuf[0], tempSizes[0], + tempBuf[1], tempSizes[1], + #ifdef _LZMA_IN_CB + tempBuf[2], tempSizes[2], + #else + inBuffer + (size_t)offset, (size_t)s3Size, + #endif + outBuffer, outSize); + RINOK(res) + } + else + return SZE_NOTIMPL; + } + return SZ_OK; +} + +SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder, + #ifdef _LZMA_IN_CB + ISzInStream *inStream, CFileSize startPos, + #else + const Byte *inBuffer, + #endif + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + Byte *tempBuf[3] = { 0, 0, 0}; + int i; + SZ_RESULT res = SzDecode2(packSizes, folder, + #ifdef _LZMA_IN_CB + inStream, startPos, + #else + inBuffer, + #endif + outBuffer, outSize, allocMain, tempBuf); + for (i = 0; i < 3; i++) + allocMain->Free(tempBuf[i]); + return res; +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.h b/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.h new file mode 100644 index 0000000..175896e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zDecode.h @@ -0,0 +1,20 @@ +/* 7zDecode.h */ + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "7zItem.h" +#include "7zAlloc.h" +#ifdef _LZMA_IN_CB +#include "7zIn.h" +#endif + +SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder, + #ifdef _LZMA_IN_CB + ISzInStream *stream, CFileSize startPos, + #else + const Byte *inBuffer, + #endif + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.c b/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.c new file mode 100644 index 0000000..1760a3c --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.c @@ -0,0 +1,119 @@ +/* 7zExtract.c */ + +#include "7zExtract.h" +#include "7zDecode.h" +#include "../../7zCrc.h" + +SZ_RESULT SzExtract( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = db->FileIndexToFolderIndexMap[fileIndex]; + SZ_RESULT res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + allocMain->Free(*outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CFolder *folder = db->Database.Folders + folderIndex; + CFileSize unPackSizeSpec = SzFolderGetUnPackSize(folder); + size_t unPackSize = (size_t)unPackSizeSpec; + CFileSize startOffset = SzArDbGetFolderStreamPos(db, folderIndex, 0); + #ifndef _LZMA_IN_CB + Byte *inBuffer = 0; + size_t processedSize; + CFileSize packSizeSpec; + size_t packSize; + RINOK(SzArDbGetFolderFullPackSize(db, folderIndex, &packSizeSpec)); + packSize = (size_t)packSizeSpec; + if (packSize != packSizeSpec) + return SZE_OUTOFMEMORY; + #endif + if (unPackSize != unPackSizeSpec) + return SZE_OUTOFMEMORY; + *blockIndex = folderIndex; + allocMain->Free(*outBuffer); + *outBuffer = 0; + + RINOK(inStream->Seek(inStream, startOffset)); + + #ifndef _LZMA_IN_CB + if (packSize != 0) + { + inBuffer = (Byte *)allocTemp->Alloc(packSize); + if (inBuffer == 0) + return SZE_OUTOFMEMORY; + } + res = inStream->Read(inStream, inBuffer, packSize, &processedSize); + if (res == SZ_OK && processedSize != packSize) + res = SZE_FAIL; + #endif + if (res == SZ_OK) + { + *outBufferSize = unPackSize; + if (unPackSize != 0) + { + *outBuffer = (Byte *)allocMain->Alloc(unPackSize); + if (*outBuffer == 0) + res = SZE_OUTOFMEMORY; + } + if (res == SZ_OK) + { + res = SzDecode(db->Database.PackSizes + + db->FolderStartPackStreamIndex[folderIndex], folder, + #ifdef _LZMA_IN_CB + inStream, startOffset, + #else + inBuffer, + #endif + *outBuffer, unPackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnPackCRCDefined) + { + if (CrcCalc(*outBuffer, unPackSize) != folder->UnPackCRC) + res = SZE_CRC_ERROR; + } + } + } + } + #ifndef _LZMA_IN_CB + allocTemp->Free(inBuffer); + #endif + } + if (res == SZ_OK) + { + UInt32 i; + CFileItem *fileItem = db->Database.Files + fileIndex; + *offset = 0; + for(i = db->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)db->Database.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZE_FAIL; + { + if (fileItem->IsFileCRCDefined) + { + if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC) + res = SZE_CRC_ERROR; + } + } + } + return res; +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.h b/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.h new file mode 100644 index 0000000..e9a4fb4 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zExtract.h @@ -0,0 +1,40 @@ +/* 7zExtract.h */ + +#ifndef __7Z_EXTRACT_H +#define __7Z_EXTRACT_H + +#include "7zIn.h" + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +SZ_RESULT SzExtract( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.c b/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.c new file mode 100644 index 0000000..3be4bc2 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.c @@ -0,0 +1,5 @@ +/* 7zHeader.c */ + +#include "7zHeader.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.h b/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.h new file mode 100644 index 0000000..3e67cf5 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zHeader.h @@ -0,0 +1,55 @@ +/* 7zHeader.h */ + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "../../Types.h" + +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; + +#define k7zMajorVersion 0 + +#define k7zStartHeaderSize 0x20 + +enum EIdEnum +{ + k7zIdEnd, + + k7zIdHeader, + + k7zIdArchiveProperties, + + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + + k7zIdPackInfo, + k7zIdUnPackInfo, + k7zIdSubStreamsInfo, + + k7zIdSize, + k7zIdCRC, + + k7zIdFolder, + + k7zIdCodersUnPackSize, + k7zIdNumUnPackStream, + + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + + k7zIdName, + k7zIdCreationTime, + k7zIdLastAccessTime, + k7zIdLastWriteTime, + k7zIdWinAttributes, + k7zIdComment, + + k7zIdEncodedHeader, + + k7zIdStartPos +}; + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zIn.c b/3rdparty/physfs/lzma/C/Archive/7z/7zIn.c new file mode 100644 index 0000000..ac25dbc --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zIn.c @@ -0,0 +1,1314 @@ +/* 7zIn.c */ + +#include "7zIn.h" +#include "7zDecode.h" +#include "../../7zCrc.h" + +#define RINOM(x) { if((x) == 0) return SZE_OUTOFMEMORY; } + +void SzArDbExInit(CArchiveDatabaseEx *db) +{ + SzArchiveDatabaseInit(&db->Database); + db->FolderStartPackStreamIndex = 0; + db->PackStreamStartPositions = 0; + db->FolderStartFileIndex = 0; + db->FileIndexToFolderIndexMap = 0; +} + +void SzArDbExFree(CArchiveDatabaseEx *db, void (*freeFunc)(void *)) +{ + freeFunc(db->FolderStartPackStreamIndex); + freeFunc(db->PackStreamStartPositions); + freeFunc(db->FolderStartFileIndex); + freeFunc(db->FileIndexToFolderIndexMap); + SzArchiveDatabaseFree(&db->Database, freeFunc); + SzArDbExInit(db); +} + +/* +CFileSize GetFolderPackStreamSize(int folderIndex, int streamIndex) const +{ + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; +} + +CFileSize GetFilePackSize(int fileIndex) const +{ + int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex >= 0) + { + const CFolder &folderInfo = Folders[folderIndex]; + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + } + return 0; +} +*/ + +#define MY_ALLOC(T, p, size, allocFunc) { if ((size) == 0) p = 0; else \ + if ((p = (T *)allocFunc((size) * sizeof(T))) == 0) return SZE_OUTOFMEMORY; } + +SZ_RESULT SzArDbExFill(CArchiveDatabaseEx *db, void * (*allocFunc)(size_t size)) +{ + UInt32 startPos = 0; + CFileSize startPosSize = 0; + UInt32 i; + UInt32 folderIndex = 0; + UInt32 indexInFolder = 0; + MY_ALLOC(UInt32, db->FolderStartPackStreamIndex, db->Database.NumFolders, allocFunc); + for(i = 0; i < db->Database.NumFolders; i++) + { + db->FolderStartPackStreamIndex[i] = startPos; + startPos += db->Database.Folders[i].NumPackStreams; + } + + MY_ALLOC(CFileSize, db->PackStreamStartPositions, db->Database.NumPackStreams, allocFunc); + + for(i = 0; i < db->Database.NumPackStreams; i++) + { + db->PackStreamStartPositions[i] = startPosSize; + startPosSize += db->Database.PackSizes[i]; + } + + MY_ALLOC(UInt32, db->FolderStartFileIndex, db->Database.NumFolders, allocFunc); + MY_ALLOC(UInt32, db->FileIndexToFolderIndexMap, db->Database.NumFiles, allocFunc); + + for (i = 0; i < db->Database.NumFiles; i++) + { + CFileItem *file = db->Database.Files + i; + int emptyStream = !file->HasStream; + if (emptyStream && indexInFolder == 0) + { + db->FileIndexToFolderIndexMap[i] = (UInt32)-1; + continue; + } + if (indexInFolder == 0) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: Loop for skipping empty folders + */ + for (;;) + { + if (folderIndex >= db->Database.NumFolders) + return SZE_ARCHIVE_ERROR; + db->FolderStartFileIndex[folderIndex] = i; + if (db->Database.Folders[folderIndex].NumUnPackStreams != 0) + break; + folderIndex++; + } + } + db->FileIndexToFolderIndexMap[i] = folderIndex; + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= db->Database.Folders[folderIndex].NumUnPackStreams) + { + folderIndex++; + indexInFolder = 0; + } + } + return SZ_OK; +} + + +CFileSize SzArDbGetFolderStreamPos(CArchiveDatabaseEx *db, UInt32 folderIndex, UInt32 indexInFolder) +{ + return db->ArchiveInfo.DataStartPosition + + db->PackStreamStartPositions[db->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; +} + +int SzArDbGetFolderFullPackSize(CArchiveDatabaseEx *db, UInt32 folderIndex, CFileSize *resSize) +{ + UInt32 packStreamIndex = db->FolderStartPackStreamIndex[folderIndex]; + CFolder *folder = db->Database.Folders + folderIndex; + CFileSize size = 0; + UInt32 i; + for (i = 0; i < folder->NumPackStreams; i++) + { + CFileSize t = size + db->Database.PackSizes[packStreamIndex + i]; + if (t < size) + return SZE_FAIL; + size = t; + } + *resSize = size; + return SZ_OK; +} + + +/* +SZ_RESULT SzReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt64 type) +{ + CBoolVector boolVector; + RINOK(ReadBoolVector2(files.Size(), boolVector)) + + CStreamSwitch streamSwitch; + RINOK(streamSwitch.Set(this, &dataVector)); + + for(int i = 0; i < files.Size(); i++) + { + CFileItem &file = files[i]; + CArchiveFileTime fileTime; + bool defined = boolVector[i]; + if (defined) + { + UInt32 low, high; + RINOK(SzReadUInt32(low)); + RINOK(SzReadUInt32(high)); + fileTime.dwLowDateTime = low; + fileTime.dwHighDateTime = high; + } + switch(type) + { + case k7zIdCreationTime: + file.IsCreationTimeDefined = defined; + if (defined) + file.CreationTime = fileTime; + break; + case k7zIdLastWriteTime: + file.IsLastWriteTimeDefined = defined; + if (defined) + file.LastWriteTime = fileTime; + break; + case k7zIdLastAccessTime: + file.IsLastAccessTimeDefined = defined; + if (defined) + file.LastAccessTime = fileTime; + break; + } + } + return SZ_OK; +} +*/ + +SZ_RESULT SafeReadDirect(ISzInStream *inStream, Byte *data, size_t size) +{ + #ifdef _LZMA_IN_CB + while (size > 0) + { + void *inBufferSpec; + size_t processedSize; + const Byte *inBuffer; + RINOK(inStream->Read(inStream, (void **)&inBufferSpec, size, &processedSize)); + inBuffer = (const Byte *)inBufferSpec; + if (processedSize == 0 || processedSize > size) + return SZE_FAIL; + size -= processedSize; + do + { + *data++ = *inBuffer++; + } + while (--processedSize != 0); + } + #else + size_t processedSize; + RINOK(inStream->Read(inStream, data, size, &processedSize)); + if (processedSize != size) + return SZE_FAIL; + #endif + return SZ_OK; +} + +SZ_RESULT SafeReadDirectByte(ISzInStream *inStream, Byte *data) +{ + return SafeReadDirect(inStream, data, 1); +} + +SZ_RESULT SafeReadDirectUInt32(ISzInStream *inStream, UInt32 *value, UInt32 *crc) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SafeReadDirectByte(inStream, &b)); + *value |= ((UInt32)b << (8 * i)); + *crc = CRC_UPDATE_BYTE(*crc, b); + } + return SZ_OK; +} + +SZ_RESULT SafeReadDirectUInt64(ISzInStream *inStream, UInt64 *value, UInt32 *crc) +{ + int i; + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + RINOK(SafeReadDirectByte(inStream, &b)); + *value |= ((UInt64)b << (8 * i)); + *crc = CRC_UPDATE_BYTE(*crc, b); + } + return SZ_OK; +} + +int TestSignatureCandidate(Byte *testBytes) +{ + size_t i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +typedef struct _CSzState +{ + Byte *Data; + size_t Size; +}CSzData; + +SZ_RESULT SzReadByte(CSzData *sd, Byte *b) +{ + if (sd->Size == 0) + return SZE_ARCHIVE_ERROR; + sd->Size--; + *b = *sd->Data++; + return SZ_OK; +} + +SZ_RESULT SzReadBytes(CSzData *sd, Byte *data, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) + { + RINOK(SzReadByte(sd, data + i)); + } + return SZ_OK; +} + +SZ_RESULT SzReadUInt32(CSzData *sd, UInt32 *value) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt32)(b) << (8 * i)); + } + return SZ_OK; +} + +SZ_RESULT SzReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte; + Byte mask = 0x80; + int i; + RINOK(SzReadByte(sd, &firstByte)); + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + *value += (highPart << (8 * i)); + return SZ_OK; + } + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + +SZ_RESULT SzReadSize(CSzData *sd, CFileSize *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + *value = (CFileSize)value64; + return SZ_OK; +} + +SZ_RESULT SzReadNumber32(CSzData *sd, UInt32 *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + if (value64 >= 0x80000000) + return SZE_NOTIMPL; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) + return SZE_NOTIMPL; + *value = (UInt32)value64; + return SZ_OK; +} + +SZ_RESULT SzReadID(CSzData *sd, UInt64 *value) +{ + return SzReadNumber(sd, value); +} + +SZ_RESULT SzSkeepDataSize(CSzData *sd, UInt64 size) +{ + if (size > sd->Size) + return SZE_ARCHIVE_ERROR; + sd->Size -= (size_t)size; + sd->Data += (size_t)size; + return SZ_OK; +} + +SZ_RESULT SzSkeepData(CSzData *sd) +{ + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + return SzSkeepDataSize(sd, size); +} + +SZ_RESULT SzReadArchiveProperties(CSzData *sd) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + SzSkeepData(sd); + } + return SZ_OK; +} + +SZ_RESULT SzWaitAttribute(CSzData *sd, UInt64 attribute) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == attribute) + return SZ_OK; + if (type == k7zIdEnd) + return SZE_ARCHIVE_ERROR; + RINOK(SzSkeepData(sd)); + } +} + +SZ_RESULT SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, void * (*allocFunc)(size_t size)) +{ + Byte b = 0; + Byte mask = 0; + size_t i; + MY_ALLOC(Byte, *v, numItems, allocFunc); + for (i = 0; i < numItems; i++) + { + if (mask == 0) + { + RINOK(SzReadByte(sd, &b)); + mask = 0x80; + } + (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); + mask >>= 1; + } + return SZ_OK; +} + +SZ_RESULT SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, void * (*allocFunc)(size_t size)) +{ + Byte allAreDefined; + size_t i; + RINOK(SzReadByte(sd, &allAreDefined)); + if (allAreDefined == 0) + return SzReadBoolVector(sd, numItems, v, allocFunc); + MY_ALLOC(Byte, *v, numItems, allocFunc); + for(i = 0; i < numItems; i++) + (*v)[i] = 1; + return SZ_OK; +} + +SZ_RESULT SzReadHashDigests( + CSzData *sd, + size_t numItems, + Byte **digestsDefined, + UInt32 **digests, + void * (*allocFunc)(size_t size)) +{ + size_t i; + RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, allocFunc)); + MY_ALLOC(UInt32, *digests, numItems, allocFunc); + for(i = 0; i < numItems; i++) + if ((*digestsDefined)[i]) + { + RINOK(SzReadUInt32(sd, (*digests) + i)); + } + return SZ_OK; +} + +SZ_RESULT SzReadPackInfo( + CSzData *sd, + CFileSize *dataOffset, + UInt32 *numPackStreams, + CFileSize **packSizes, + Byte **packCRCsDefined, + UInt32 **packCRCs, + void * (*allocFunc)(size_t size)) +{ + UInt32 i; + RINOK(SzReadSize(sd, dataOffset)); + RINOK(SzReadNumber32(sd, numPackStreams)); + + RINOK(SzWaitAttribute(sd, k7zIdSize)); + + MY_ALLOC(CFileSize, *packSizes, (size_t)*numPackStreams, allocFunc); + + for(i = 0; i < *numPackStreams; i++) + { + RINOK(SzReadSize(sd, (*packSizes) + i)); + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + if (type == k7zIdCRC) + { + RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, allocFunc)); + continue; + } + RINOK(SzSkeepData(sd)); + } + if (*packCRCsDefined == 0) + { + MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, allocFunc); + MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, allocFunc); + for(i = 0; i < *numPackStreams; i++) + { + (*packCRCsDefined)[i] = 0; + (*packCRCs)[i] = 0; + } + } + return SZ_OK; +} + +SZ_RESULT SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZE_ARCHIVE_ERROR; +} + +SZ_RESULT SzGetNextFolderItem(CSzData *sd, CFolder *folder, void * (*allocFunc)(size_t size)) +{ + UInt32 numCoders; + UInt32 numBindPairs; + UInt32 numPackedStreams; + UInt32 i; + UInt32 numInStreams = 0; + UInt32 numOutStreams = 0; + RINOK(SzReadNumber32(sd, &numCoders)); + folder->NumCoders = numCoders; + + MY_ALLOC(CCoderInfo, folder->Coders, (size_t)numCoders, allocFunc); + + for (i = 0; i < numCoders; i++) + SzCoderInfoInit(folder->Coders + i); + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CCoderInfo *coder = folder->Coders + i; + { + unsigned idSize, j; + Byte longID[15]; + RINOK(SzReadByte(sd, &mainByte)); + idSize = (unsigned)(mainByte & 0xF); + RINOK(SzReadBytes(sd, longID, idSize)); + if (idSize > sizeof(coder->MethodID)) + return SZE_NOTIMPL; + coder->MethodID = 0; + for (j = 0; j < idSize; j++) + coder->MethodID |= (CMethodID)longID[idSize - 1 - j] << (8 * j); + + if ((mainByte & 0x10) != 0) + { + RINOK(SzReadNumber32(sd, &coder->NumInStreams)); + RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); + } + else + { + coder->NumInStreams = 1; + coder->NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + if (!SzByteBufferCreate(&coder->Properties, (size_t)propertiesSize, allocFunc)) + return SZE_OUTOFMEMORY; + RINOK(SzReadBytes(sd, coder->Properties.Items, (size_t)propertiesSize)); + } + } + while ((mainByte & 0x80) != 0) + { + RINOK(SzReadByte(sd, &mainByte)); + RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); + if ((mainByte & 0x10) != 0) + { + UInt32 n; + RINOK(SzReadNumber32(sd, &n)); + RINOK(SzReadNumber32(sd, &n)); + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + RINOK(SzSkeepDataSize(sd, propertiesSize)); + } + } + numInStreams += (UInt32)coder->NumInStreams; + numOutStreams += (UInt32)coder->NumOutStreams; + } + + numBindPairs = numOutStreams - 1; + folder->NumBindPairs = numBindPairs; + + + MY_ALLOC(CBindPair, folder->BindPairs, (size_t)numBindPairs, allocFunc); + + for (i = 0; i < numBindPairs; i++) + { + CBindPair *bindPair = folder->BindPairs + i;; + RINOK(SzReadNumber32(sd, &bindPair->InIndex)); + RINOK(SzReadNumber32(sd, &bindPair->OutIndex)); + } + + numPackedStreams = numInStreams - (UInt32)numBindPairs; + + folder->NumPackStreams = numPackedStreams; + MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackedStreams, allocFunc); + + if (numPackedStreams == 1) + { + UInt32 j; + UInt32 pi = 0; + for (j = 0; j < numInStreams; j++) + if (SzFolderFindBindPairForInStream(folder, j) < 0) + { + folder->PackStreams[pi++] = j; + break; + } + } + else + for(i = 0; i < numPackedStreams; i++) + { + RINOK(SzReadNumber32(sd, folder->PackStreams + i)); + } + return SZ_OK; +} + +SZ_RESULT SzReadUnPackInfo( + CSzData *sd, + UInt32 *numFolders, + CFolder **folders, /* for allocFunc */ + void * (*allocFunc)(size_t size), + ISzAlloc *allocTemp) +{ + UInt32 i; + RINOK(SzWaitAttribute(sd, k7zIdFolder)); + RINOK(SzReadNumber32(sd, numFolders)); + { + RINOK(SzReadSwitch(sd)); + + MY_ALLOC(CFolder, *folders, (size_t)*numFolders, allocFunc); + + for(i = 0; i < *numFolders; i++) + SzFolderInit((*folders) + i); + + for(i = 0; i < *numFolders; i++) + { + RINOK(SzGetNextFolderItem(sd, (*folders) + i, allocFunc)); + } + } + + RINOK(SzWaitAttribute(sd, k7zIdCodersUnPackSize)); + + for(i = 0; i < *numFolders; i++) + { + UInt32 j; + CFolder *folder = (*folders) + i; + UInt32 numOutStreams = SzFolderGetNumOutStreams(folder); + + MY_ALLOC(CFileSize, folder->UnPackSizes, (size_t)numOutStreams, allocFunc); + + for(j = 0; j < numOutStreams; j++) + { + RINOK(SzReadSize(sd, folder->UnPackSizes + j)); + } + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + SZ_RESULT res; + Byte *crcsDefined = 0; + UInt32 *crcs = 0; + res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp->Alloc); + if (res == SZ_OK) + { + for(i = 0; i < *numFolders; i++) + { + CFolder *folder = (*folders) + i; + folder->UnPackCRCDefined = crcsDefined[i]; + folder->UnPackCRC = crcs[i]; + } + } + allocTemp->Free(crcs); + allocTemp->Free(crcsDefined); + RINOK(res); + continue; + } + RINOK(SzSkeepData(sd)); + } +} + +SZ_RESULT SzReadSubStreamsInfo( + CSzData *sd, + UInt32 numFolders, + CFolder *folders, + UInt32 *numUnPackStreams, + CFileSize **unPackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + UInt64 type = 0; + UInt32 i; + UInt32 si = 0; + UInt32 numDigests = 0; + + for(i = 0; i < numFolders; i++) + folders[i].NumUnPackStreams = 1; + *numUnPackStreams = numFolders; + + for (;;) + { + RINOK(SzReadID(sd, &type)); + if (type == k7zIdNumUnPackStream) + { + *numUnPackStreams = 0; + for(i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + folders[i].NumUnPackStreams = numStreams; + *numUnPackStreams += numStreams; + } + continue; + } + if (type == k7zIdCRC || type == k7zIdSize) + break; + if (type == k7zIdEnd) + break; + RINOK(SzSkeepData(sd)); + } + + if (*numUnPackStreams == 0) + { + *unPackSizes = 0; + *digestsDefined = 0; + *digests = 0; + } + else + { + *unPackSizes = (CFileSize *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(CFileSize)); + RINOM(*unPackSizes); + *digestsDefined = (Byte *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(Byte)); + RINOM(*digestsDefined); + *digests = (UInt32 *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(UInt32)); + RINOM(*digests); + } + + for(i = 0; i < numFolders; i++) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: we check that folder is empty + */ + CFileSize sum = 0; + UInt32 j; + UInt32 numSubstreams = folders[i].NumUnPackStreams; + if (numSubstreams == 0) + continue; + if (type == k7zIdSize) + for (j = 1; j < numSubstreams; j++) + { + CFileSize size; + RINOK(SzReadSize(sd, &size)); + (*unPackSizes)[si++] = size; + sum += size; + } + (*unPackSizes)[si++] = SzFolderGetUnPackSize(folders + i) - sum; + } + if (type == k7zIdSize) + { + RINOK(SzReadID(sd, &type)); + } + + for(i = 0; i < *numUnPackStreams; i++) + { + (*digestsDefined)[i] = 0; + (*digests)[i] = 0; + } + + + for(i = 0; i < numFolders; i++) + { + UInt32 numSubstreams = folders[i].NumUnPackStreams; + if (numSubstreams != 1 || !folders[i].UnPackCRCDefined) + numDigests += numSubstreams; + } + + + si = 0; + for (;;) + { + if (type == k7zIdCRC) + { + int digestIndex = 0; + Byte *digestsDefined2 = 0; + UInt32 *digests2 = 0; + SZ_RESULT res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp->Alloc); + if (res == SZ_OK) + { + for (i = 0; i < numFolders; i++) + { + CFolder *folder = folders + i; + UInt32 numSubstreams = folder->NumUnPackStreams; + if (numSubstreams == 1 && folder->UnPackCRCDefined) + { + (*digestsDefined)[si] = 1; + (*digests)[si] = folder->UnPackCRC; + si++; + } + else + { + UInt32 j; + for (j = 0; j < numSubstreams; j++, digestIndex++) + { + (*digestsDefined)[si] = digestsDefined2[digestIndex]; + (*digests)[si] = digests2[digestIndex]; + si++; + } + } + } + } + allocTemp->Free(digestsDefined2); + allocTemp->Free(digests2); + RINOK(res); + } + else if (type == k7zIdEnd) + return SZ_OK; + else + { + RINOK(SzSkeepData(sd)); + } + RINOK(SzReadID(sd, &type)); + } +} + + +SZ_RESULT SzReadStreamsInfo( + CSzData *sd, + CFileSize *dataOffset, + CArchiveDatabase *db, + UInt32 *numUnPackStreams, + CFileSize **unPackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + void * (*allocFunc)(size_t size), + ISzAlloc *allocTemp) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if ((UInt64)(int)type != type) + return SZE_FAIL; + switch((int)type) + { + case k7zIdEnd: + return SZ_OK; + case k7zIdPackInfo: + { + RINOK(SzReadPackInfo(sd, dataOffset, &db->NumPackStreams, + &db->PackSizes, &db->PackCRCsDefined, &db->PackCRCs, allocFunc)); + break; + } + case k7zIdUnPackInfo: + { + RINOK(SzReadUnPackInfo(sd, &db->NumFolders, &db->Folders, allocFunc, allocTemp)); + break; + } + case k7zIdSubStreamsInfo: + { + RINOK(SzReadSubStreamsInfo(sd, db->NumFolders, db->Folders, + numUnPackStreams, unPackSizes, digestsDefined, digests, allocTemp)); + break; + } + default: + return SZE_FAIL; + } + } +} + +Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +SZ_RESULT SzReadFileNames(CSzData *sd, UInt32 numFiles, CFileItem *files, + void * (*allocFunc)(size_t size)) +{ + UInt32 i; + for(i = 0; i < numFiles; i++) + { + UInt32 len = 0; + UInt32 pos = 0; + CFileItem *file = files + i; + while(pos + 2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + len++; + if (value == 0) + break; + if (value < 0x80) + continue; + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2; + if (value >= 0xDC00) + return SZE_ARCHIVE_ERROR; + if (pos + 2 > sd->Size) + return SZE_ARCHIVE_ERROR; + c2 = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + if (c2 < 0xDC00 || c2 >= 0xE000) + return SZE_ARCHIVE_ERROR; + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + len += numAdds; + } + + MY_ALLOC(char, file->Name, (size_t)len, allocFunc); + + len = 0; + while(2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + if (value < 0x80) + { + file->Name[len++] = (char)value; + if (value == 0) + break; + continue; + } + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2 = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + file->Name[len++] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); + do + { + numAdds--; + file->Name[len++] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); + } + while(numAdds > 0); + + len += numAdds; + } + } + return SZ_OK; +} + +SZ_RESULT SzReadHeader2( + CSzData *sd, + CArchiveDatabaseEx *db, /* allocMain */ + CFileSize **unPackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + Byte **emptyStreamVector, /* allocTemp */ + Byte **emptyFileVector, /* allocTemp */ + Byte **lwtVector, /* allocTemp */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 type; + UInt32 numUnPackStreams = 0; + UInt32 numFiles = 0; + CFileItem *files = 0; + UInt32 numEmptyStreams = 0; + UInt32 i; + + RINOK(SzReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + RINOK(SzReadArchiveProperties(sd)); + RINOK(SzReadID(sd, &type)); + } + + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(sd, + &db->ArchiveInfo.DataStartPosition, + &db->Database, + &numUnPackStreams, + unPackSizes, + digestsDefined, + digests, allocMain->Alloc, allocTemp)); + db->ArchiveInfo.DataStartPosition += db->ArchiveInfo.StartPositionAfterHeader; + RINOK(SzReadID(sd, &type)); + } + + if (type == k7zIdEnd) + return SZ_OK; + if (type != k7zIdFilesInfo) + return SZE_ARCHIVE_ERROR; + + RINOK(SzReadNumber32(sd, &numFiles)); + db->Database.NumFiles = numFiles; + + MY_ALLOC(CFileItem, files, (size_t)numFiles, allocMain->Alloc); + + db->Database.Files = files; + for(i = 0; i < numFiles; i++) + SzFileInit(files + i); + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SzReadNumber(sd, &size)); + + if ((UInt64)(int)type != type) + { + RINOK(SzSkeepDataSize(sd, size)); + } + else + switch((int)type) + { + case k7zIdName: + { + RINOK(SzReadSwitch(sd)); + RINOK(SzReadFileNames(sd, numFiles, files, allocMain->Alloc)) + break; + } + case k7zIdEmptyStream: + { + RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp->Alloc)); + numEmptyStreams = 0; + for (i = 0; i < numFiles; i++) + if ((*emptyStreamVector)[i]) + numEmptyStreams++; + break; + } + case k7zIdEmptyFile: + { + RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp->Alloc)); + break; + } + case k7zIdLastWriteTime: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp->Alloc)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->IsLastWriteTimeDefined = defined; + f->LastWriteTime.Low = f->LastWriteTime.High = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->LastWriteTime.Low)); + RINOK(SzReadUInt32(sd, &f->LastWriteTime.High)); + } + } + break; + } + default: + { + RINOK(SzSkeepDataSize(sd, size)); + } + } + } + + { + UInt32 emptyFileIndex = 0; + UInt32 sizeIndex = 0; + for(i = 0; i < numFiles; i++) + { + CFileItem *file = files + i; + file->IsAnti = 0; + if (*emptyStreamVector == 0) + file->HasStream = 1; + else + file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); + if(file->HasStream) + { + file->IsDirectory = 0; + file->Size = (*unPackSizes)[sizeIndex]; + file->FileCRC = (*digests)[sizeIndex]; + file->IsFileCRCDefined = (Byte)(*digestsDefined)[sizeIndex]; + sizeIndex++; + } + else + { + if (*emptyFileVector == 0) + file->IsDirectory = 1; + else + file->IsDirectory = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); + emptyFileIndex++; + file->Size = 0; + file->IsFileCRCDefined = 0; + } + } + } + return SzArDbExFill(db, allocMain->Alloc); +} + +SZ_RESULT SzReadHeader( + CSzData *sd, + CArchiveDatabaseEx *db, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + CFileSize *unPackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + Byte *emptyStreamVector = 0; + Byte *emptyFileVector = 0; + Byte *lwtVector = 0; + SZ_RESULT res = SzReadHeader2(sd, db, + &unPackSizes, &digestsDefined, &digests, + &emptyStreamVector, &emptyFileVector, &lwtVector, + allocMain, allocTemp); + allocTemp->Free(unPackSizes); + allocTemp->Free(digestsDefined); + allocTemp->Free(digests); + allocTemp->Free(emptyStreamVector); + allocTemp->Free(emptyFileVector); + allocTemp->Free(lwtVector); + return res; +} + +SZ_RESULT SzReadAndDecodePackedStreams2( + ISzInStream *inStream, + CSzData *sd, + CSzByteBuffer *outBuffer, + CFileSize baseOffset, + CArchiveDatabase *db, + CFileSize **unPackSizes, + Byte **digestsDefined, + UInt32 **digests, + #ifndef _LZMA_IN_CB + Byte **inBuffer, + #endif + ISzAlloc *allocTemp) +{ + + UInt32 numUnPackStreams = 0; + CFileSize dataStartPos; + CFolder *folder; + #ifndef _LZMA_IN_CB + CFileSize packSize = 0; + UInt32 i = 0; + #endif + CFileSize unPackSize; + SZ_RESULT res; + + RINOK(SzReadStreamsInfo(sd, &dataStartPos, db, + &numUnPackStreams, unPackSizes, digestsDefined, digests, + allocTemp->Alloc, allocTemp)); + + dataStartPos += baseOffset; + if (db->NumFolders != 1) + return SZE_ARCHIVE_ERROR; + + folder = db->Folders; + unPackSize = SzFolderGetUnPackSize(folder); + + RINOK(inStream->Seek(inStream, dataStartPos)); + + #ifndef _LZMA_IN_CB + for (i = 0; i < db->NumPackStreams; i++) + packSize += db->PackSizes[i]; + + MY_ALLOC(Byte, *inBuffer, (size_t)packSize, allocTemp->Alloc); + + RINOK(SafeReadDirect(inStream, *inBuffer, (size_t)packSize)); + #endif + + if (!SzByteBufferCreate(outBuffer, (size_t)unPackSize, allocTemp->Alloc)) + return SZE_OUTOFMEMORY; + + res = SzDecode(db->PackSizes, folder, + #ifdef _LZMA_IN_CB + inStream, dataStartPos, + #else + *inBuffer, + #endif + outBuffer->Items, (size_t)unPackSize, allocTemp); + RINOK(res) + if (folder->UnPackCRCDefined) + if (CrcCalc(outBuffer->Items, (size_t)unPackSize) != folder->UnPackCRC) + return SZE_FAIL; + return SZ_OK; +} + +SZ_RESULT SzReadAndDecodePackedStreams( + ISzInStream *inStream, + CSzData *sd, + CSzByteBuffer *outBuffer, + CFileSize baseOffset, + ISzAlloc *allocTemp) +{ + CArchiveDatabase db; + CFileSize *unPackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + #ifndef _LZMA_IN_CB + Byte *inBuffer = 0; + #endif + SZ_RESULT res; + SzArchiveDatabaseInit(&db); + res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, + &db, &unPackSizes, &digestsDefined, &digests, + #ifndef _LZMA_IN_CB + &inBuffer, + #endif + allocTemp); + SzArchiveDatabaseFree(&db, allocTemp->Free); + allocTemp->Free(unPackSizes); + allocTemp->Free(digestsDefined); + allocTemp->Free(digests); + #ifndef _LZMA_IN_CB + allocTemp->Free(inBuffer); + #endif + return res; +} + +SZ_RESULT SzArchiveOpen2( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte signature[k7zSignatureSize]; + Byte version; + UInt32 crcFromArchive; + UInt64 nextHeaderOffset; + UInt64 nextHeaderSize; + UInt32 nextHeaderCRC; + UInt32 crc = 0; + CFileSize pos = 0; + CSzByteBuffer buffer; + CSzData sd; + SZ_RESULT res; + + RINOK(SafeReadDirect(inStream, signature, k7zSignatureSize)); + + if (!TestSignatureCandidate(signature)) + return SZE_ARCHIVE_ERROR; + + /* + db.Clear(); + db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; + */ + RINOK(SafeReadDirectByte(inStream, &version)); + if (version != k7zMajorVersion) + return SZE_ARCHIVE_ERROR; + RINOK(SafeReadDirectByte(inStream, &version)); + + RINOK(SafeReadDirectUInt32(inStream, &crcFromArchive, &crc)); + + crc = CRC_INIT_VAL; + RINOK(SafeReadDirectUInt64(inStream, &nextHeaderOffset, &crc)); + RINOK(SafeReadDirectUInt64(inStream, &nextHeaderSize, &crc)); + RINOK(SafeReadDirectUInt32(inStream, &nextHeaderCRC, &crc)); + + pos = k7zStartHeaderSize; + db->ArchiveInfo.StartPositionAfterHeader = pos; + + if (CRC_GET_DIGEST(crc) != crcFromArchive) + return SZE_ARCHIVE_ERROR; + + if (nextHeaderSize == 0) + return SZ_OK; + + RINOK(inStream->Seek(inStream, (CFileSize)(pos + nextHeaderOffset))); + + if (!SzByteBufferCreate(&buffer, (size_t)nextHeaderSize, allocTemp->Alloc)) + return SZE_OUTOFMEMORY; + + res = SafeReadDirect(inStream, buffer.Items, (size_t)nextHeaderSize); + if (res == SZ_OK) + { + res = SZE_ARCHIVE_ERROR; + if (CrcCalc(buffer.Items, (UInt32)nextHeaderSize) == nextHeaderCRC) + { + for (;;) + { + UInt64 type; + sd.Data = buffer.Items; + sd.Size = buffer.Capacity; + res = SzReadID(&sd, &type); + if (res != SZ_OK) + break; + if (type == k7zIdHeader) + { + res = SzReadHeader(&sd, db, allocMain, allocTemp); + break; + } + if (type != k7zIdEncodedHeader) + { + res = SZE_ARCHIVE_ERROR; + break; + } + { + CSzByteBuffer outBuffer; + res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, + db->ArchiveInfo.StartPositionAfterHeader, + allocTemp); + if (res != SZ_OK) + { + SzByteBufferFree(&outBuffer, allocTemp->Free); + break; + } + SzByteBufferFree(&buffer, allocTemp->Free); + buffer.Items = outBuffer.Items; + buffer.Capacity = outBuffer.Capacity; + } + } + } + } + SzByteBufferFree(&buffer, allocTemp->Free); + return res; +} + +SZ_RESULT SzArchiveOpen( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + SZ_RESULT res = SzArchiveOpen2(inStream, db, allocMain, allocTemp); + if (res != SZ_OK) + SzArDbExFree(db, allocMain->Free); + return res; +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zIn.h b/3rdparty/physfs/lzma/C/Archive/7z/7zIn.h new file mode 100644 index 0000000..0b4ca08 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zIn.h @@ -0,0 +1,55 @@ +/* 7zIn.h */ + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "7zHeader.h" +#include "7zItem.h" +#include "7zAlloc.h" + +typedef struct _CInArchiveInfo +{ + CFileSize StartPositionAfterHeader; + CFileSize DataStartPosition; +}CInArchiveInfo; + +typedef struct _CArchiveDatabaseEx +{ + CArchiveDatabase Database; + CInArchiveInfo ArchiveInfo; + UInt32 *FolderStartPackStreamIndex; + CFileSize *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; +}CArchiveDatabaseEx; + +void SzArDbExInit(CArchiveDatabaseEx *db); +void SzArDbExFree(CArchiveDatabaseEx *db, void (*freeFunc)(void *)); +CFileSize SzArDbGetFolderStreamPos(CArchiveDatabaseEx *db, UInt32 folderIndex, UInt32 indexInFolder); +int SzArDbGetFolderFullPackSize(CArchiveDatabaseEx *db, UInt32 folderIndex, CFileSize *resSize); + +typedef struct _ISzInStream +{ + #ifdef _LZMA_IN_CB + SZ_RESULT (*Read)( + void *object, /* pointer to ISzInStream itself */ + void **buffer, /* out: pointer to buffer with data */ + size_t maxRequiredSize, /* max required size to read */ + size_t *processedSize); /* real processed size. + processedSize can be less than maxRequiredSize. + If processedSize == 0, then there are no more + bytes in stream. */ + #else + SZ_RESULT (*Read)(void *object, void *buffer, size_t size, size_t *processedSize); + #endif + SZ_RESULT (*Seek)(void *object, CFileSize pos); +} ISzInStream; + + +int SzArchiveOpen( + ISzInStream *inStream, + CArchiveDatabaseEx *db, + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zItem.c b/3rdparty/physfs/lzma/C/Archive/7z/7zItem.c new file mode 100644 index 0000000..a88afe9 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zItem.c @@ -0,0 +1,134 @@ +/* 7zItem.c */ + +#include "7zItem.h" +#include "7zAlloc.h" + +void SzCoderInfoInit(CCoderInfo *coder) +{ + SzByteBufferInit(&coder->Properties); +} + +void SzCoderInfoFree(CCoderInfo *coder, void (*freeFunc)(void *p)) +{ + SzByteBufferFree(&coder->Properties, freeFunc); + SzCoderInfoInit(coder); +} + +void SzFolderInit(CFolder *folder) +{ + folder->NumCoders = 0; + folder->Coders = 0; + folder->NumBindPairs = 0; + folder->BindPairs = 0; + folder->NumPackStreams = 0; + folder->PackStreams = 0; + folder->UnPackSizes = 0; + folder->UnPackCRCDefined = 0; + folder->UnPackCRC = 0; + folder->NumUnPackStreams = 0; +} + +void SzFolderFree(CFolder *folder, void (*freeFunc)(void *p)) +{ + UInt32 i; + for (i = 0; i < folder->NumCoders; i++) + SzCoderInfoFree(&folder->Coders[i], freeFunc); + freeFunc(folder->Coders); + freeFunc(folder->BindPairs); + freeFunc(folder->PackStreams); + freeFunc(folder->UnPackSizes); + SzFolderInit(folder); +} + +UInt32 SzFolderGetNumOutStreams(CFolder *folder) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < folder->NumCoders; i++) + result += folder->Coders[i].NumOutStreams; + return result; +} + +int SzFolderFindBindPairForInStream(CFolder *folder, UInt32 inStreamIndex) +{ + UInt32 i; + for(i = 0; i < folder->NumBindPairs; i++) + if (folder->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +int SzFolderFindBindPairForOutStream(CFolder *folder, UInt32 outStreamIndex) +{ + UInt32 i; + for(i = 0; i < folder->NumBindPairs; i++) + if (folder->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +CFileSize SzFolderGetUnPackSize(CFolder *folder) +{ + int i = (int)SzFolderGetNumOutStreams(folder); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolderFindBindPairForOutStream(folder, i) < 0) + return folder->UnPackSizes[i]; + /* throw 1; */ + return 0; +} + +/* +int FindPackStreamArrayIndex(int inStreamIndex) const +{ + for(int i = 0; i < PackStreams.Size(); i++) + if (PackStreams[i] == inStreamIndex) + return i; + return -1; +} +*/ + +void SzFileInit(CFileItem *fileItem) +{ + fileItem->IsFileCRCDefined = 0; + fileItem->HasStream = 1; + fileItem->IsDirectory = 0; + fileItem->IsAnti = 0; + fileItem->IsLastWriteTimeDefined = 0; + fileItem->Name = 0; +} + +void SzFileFree(CFileItem *fileItem, void (*freeFunc)(void *p)) +{ + freeFunc(fileItem->Name); + SzFileInit(fileItem); +} + +void SzArchiveDatabaseInit(CArchiveDatabase *db) +{ + db->NumPackStreams = 0; + db->PackSizes = 0; + db->PackCRCsDefined = 0; + db->PackCRCs = 0; + db->NumFolders = 0; + db->Folders = 0; + db->NumFiles = 0; + db->Files = 0; +} + +void SzArchiveDatabaseFree(CArchiveDatabase *db, void (*freeFunc)(void *)) +{ + UInt32 i; + for (i = 0; i < db->NumFolders; i++) + SzFolderFree(&db->Folders[i], freeFunc); + for (i = 0; i < db->NumFiles; i++) + SzFileFree(&db->Files[i], freeFunc); + freeFunc(db->PackSizes); + freeFunc(db->PackCRCsDefined); + freeFunc(db->PackCRCs); + freeFunc(db->Folders); + freeFunc(db->Files); + SzArchiveDatabaseInit(db); +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zItem.h b/3rdparty/physfs/lzma/C/Archive/7z/7zItem.h new file mode 100644 index 0000000..05567bf --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zItem.h @@ -0,0 +1,95 @@ +/* 7zItem.h */ + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "7zMethodID.h" +#include "7zHeader.h" +#include "7zBuffer.h" + +typedef struct _CCoderInfo +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + CMethodID MethodID; + CSzByteBuffer Properties; +}CCoderInfo; + +void SzCoderInfoInit(CCoderInfo *coder); +void SzCoderInfoFree(CCoderInfo *coder, void (*freeFunc)(void *p)); + +typedef struct _CBindPair +{ + UInt32 InIndex; + UInt32 OutIndex; +}CBindPair; + +typedef struct _CFolder +{ + UInt32 NumCoders; + CCoderInfo *Coders; + UInt32 NumBindPairs; + CBindPair *BindPairs; + UInt32 NumPackStreams; + UInt32 *PackStreams; + CFileSize *UnPackSizes; + int UnPackCRCDefined; + UInt32 UnPackCRC; + + UInt32 NumUnPackStreams; +}CFolder; + +void SzFolderInit(CFolder *folder); +CFileSize SzFolderGetUnPackSize(CFolder *folder); +int SzFolderFindBindPairForInStream(CFolder *folder, UInt32 inStreamIndex); +UInt32 SzFolderGetNumOutStreams(CFolder *folder); +CFileSize SzFolderGetUnPackSize(CFolder *folder); + +typedef struct _CArchiveFileTime +{ + UInt32 Low; + UInt32 High; +} CArchiveFileTime; + +typedef struct _CFileItem +{ + CArchiveFileTime LastWriteTime; + /* + CFileSize StartPos; + UInt32 Attributes; + */ + CFileSize Size; + UInt32 FileCRC; + char *Name; + + Byte IsFileCRCDefined; + Byte HasStream; + Byte IsDirectory; + Byte IsAnti; + Byte IsLastWriteTimeDefined; + /* + int AreAttributesDefined; + int IsLastWriteTimeDefined; + int IsStartPosDefined; + */ +}CFileItem; + +void SzFileInit(CFileItem *fileItem); + +typedef struct _CArchiveDatabase +{ + UInt32 NumPackStreams; + CFileSize *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + UInt32 NumFolders; + CFolder *Folders; + UInt32 NumFiles; + CFileItem *Files; +}CArchiveDatabase; + +void SzArchiveDatabaseInit(CArchiveDatabase *db); +void SzArchiveDatabaseFree(CArchiveDatabase *db, void (*freeFunc)(void *)); + + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zMain.c b/3rdparty/physfs/lzma/C/Archive/7z/7zMain.c new file mode 100644 index 0000000..0deef89 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zMain.c @@ -0,0 +1,428 @@ +/* +7zMain.c +Test application for 7z Decoder +LZMA SDK 4.43 Copyright (c) 1999-2006 Igor Pavlov (2006-06-04) +*/ + +#include +#include +#include + +#ifdef _WIN32 +#define USE_WINDOWS_FUNCTIONS +#endif + +#ifdef USE_WINDOWS_FUNCTIONS +#include +#endif + +#include "7zIn.h" +#include "7zExtract.h" + +#include "../../7zCrc.h" + + +#ifdef USE_WINDOWS_FUNCTIONS +typedef HANDLE MY_FILE_HANDLE; +#else +typedef FILE *MY_FILE_HANDLE; +#endif + +void ConvertNumberToString(CFileSize value, char *s) +{ + char temp[32]; + int pos = 0; + do + { + temp[pos++] = (char)('0' + (int)(value % 10)); + value /= 10; + } + while (value != 0); + do + *s++ = temp[--pos]; + while(pos > 0); + *s = '\0'; +} + +#define PERIOD_4 (4 * 365 + 1) +#define PERIOD_100 (PERIOD_4 * 25 - 1) +#define PERIOD_400 (PERIOD_100 * 4 + 1) + +void ConvertFileTimeToString(CArchiveFileTime *ft, char *s) +{ + unsigned year, mon, day, hour, min, sec; + UInt64 v64 = ft->Low | ((UInt64)ft->High << 32); + Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + unsigned temp; + UInt32 v; + v64 /= 10000000; + sec = (unsigned)(v64 % 60); + v64 /= 60; + min = (unsigned)(v64 % 60); + v64 /= 60; + hour = (unsigned)(v64 % 24); + v64 /= 24; + + v = (UInt32)v64; + + year = (unsigned)(1601 + v / PERIOD_400 * 400); + v %= PERIOD_400; + + temp = (unsigned)(v / PERIOD_100); + if (temp == 4) + temp = 3; + year += temp * 100; + v -= temp * PERIOD_100; + + temp = v / PERIOD_4; + if (temp == 25) + temp = 24; + year += temp * 4; + v -= temp * PERIOD_4; + + temp = v / 365; + if (temp == 4) + temp = 3; + year += temp; + v -= temp * 365; + + if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + ms[1] = 29; + for (mon = 1; mon <= 12; mon++) + { + unsigned s = ms[mon - 1]; + if (v < s) + break; + v -= s; + } + day = (unsigned)v + 1; + sprintf(s, "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec); +} + + +#ifdef USE_WINDOWS_FUNCTIONS +/* + ReadFile and WriteFile functions in Windows have BUG: + If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) + from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES + (Insufficient system resources exist to complete the requested service). +*/ +#define kChunkSizeMax (1 << 24) +#endif + +size_t MyReadFile(MY_FILE_HANDLE file, void *data, size_t size) +{ + if (size == 0) + return 0; + #ifdef USE_WINDOWS_FUNCTIONS + { + size_t processedSize = 0; + do + { + DWORD curSize = (size > kChunkSizeMax) ? kChunkSizeMax : (DWORD)size; + DWORD processedLoc = 0; + BOOL res = ReadFile(file, data, curSize, &processedLoc, NULL); + data = (void *)((unsigned char *)data + processedLoc); + size -= processedLoc; + processedSize += processedLoc; + if (!res || processedLoc == 0) + break; + } + while (size > 0); + return processedSize; + } + #else + return fread(data, 1, size, file); + #endif +} + +size_t MyWriteFile(MY_FILE_HANDLE file, void *data, size_t size) +{ + if (size == 0) + return 0; + #ifdef USE_WINDOWS_FUNCTIONS + { + size_t processedSize = 0; + do + { + DWORD curSize = (size > kChunkSizeMax) ? kChunkSizeMax : (DWORD)size; + DWORD processedLoc = 0; + BOOL res = WriteFile(file, data, curSize, &processedLoc, NULL); + data = (void *)((unsigned char *)data + processedLoc); + size -= processedLoc; + processedSize += processedLoc; + if (!res) + break; + } + while (size > 0); + return processedSize; + } + #else + return fwrite(data, 1, size, file); + #endif +} + +int MyCloseFile(MY_FILE_HANDLE file) +{ + #ifdef USE_WINDOWS_FUNCTIONS + return (CloseHandle(file) != FALSE) ? 0 : 1; + #else + return fclose(file); + #endif +} + +typedef struct _CFileInStream +{ + ISzInStream InStream; + MY_FILE_HANDLE File; +} CFileInStream; + +#ifdef _LZMA_IN_CB + +#define kBufferSize (1 << 12) +Byte g_Buffer[kBufferSize]; + +SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxRequiredSize, size_t *processedSize) +{ + CFileInStream *s = (CFileInStream *)object; + size_t processedSizeLoc; + if (maxRequiredSize > kBufferSize) + maxRequiredSize = kBufferSize; + processedSizeLoc = MyReadFile(s->File, g_Buffer, maxRequiredSize); + *buffer = g_Buffer; + if (processedSize != 0) + *processedSize = processedSizeLoc; + return SZ_OK; +} + +#else + +SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size, size_t *processedSize) +{ + CFileInStream *s = (CFileInStream *)object; + size_t processedSizeLoc = MyReadFile(s->File, buffer, size); + if (processedSize != 0) + *processedSize = processedSizeLoc; + return SZ_OK; +} + +#endif + +SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) +{ + CFileInStream *s = (CFileInStream *)object; + + #ifdef USE_WINDOWS_FUNCTIONS + { + LARGE_INTEGER value; + value.LowPart = (DWORD)pos; + value.HighPart = (LONG)((UInt64)pos >> 32); + #ifdef _SZ_FILE_SIZE_32 + /* VC 6.0 has bug with >> 32 shifts. */ + value.HighPart = 0; + #endif + value.LowPart = SetFilePointer(s->File, value.LowPart, &value.HighPart, FILE_BEGIN); + if (value.LowPart == 0xFFFFFFFF) + if(GetLastError() != NO_ERROR) + return SZE_FAIL; + return SZ_OK; + } + #else + int res = fseek(s->File, (long)pos, SEEK_SET); + if (res == 0) + return SZ_OK; + return SZE_FAIL; + #endif +} + +void PrintError(char *sz) +{ + printf("\nERROR: %s\n", sz); +} + +int main(int numargs, char *args[]) +{ + CFileInStream archiveStream; + CArchiveDatabaseEx db; + SZ_RESULT res; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + + printf("\n7z ANSI-C Decoder 4.48 Copyright (c) 1999-2007 Igor Pavlov 2007-06-21\n"); + if (numargs == 1) + { + printf( + "\nUsage: 7zDec \n\n" + "\n" + " e: Extract files from archive\n" + " l: List contents of archive\n" + " t: Test integrity of archive\n"); + return 0; + } + if (numargs < 3) + { + PrintError("incorrect command"); + return 1; + } + + archiveStream.File = + #ifdef USE_WINDOWS_FUNCTIONS + CreateFile(args[2], GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (archiveStream.File == INVALID_HANDLE_VALUE) + #else + archiveStream.File = fopen(args[2], "rb"); + if (archiveStream.File == 0) + #endif + { + PrintError("can not open input file"); + return 1; + } + + archiveStream.InStream.Read = SzFileReadImp; + archiveStream.InStream.Seek = SzFileSeekImp; + + allocImp.Alloc = SzAlloc; + allocImp.Free = SzFree; + + allocTempImp.Alloc = SzAllocTemp; + allocTempImp.Free = SzFreeTemp; + + CrcGenerateTable(); + + SzArDbExInit(&db); + res = SzArchiveOpen(&archiveStream.InStream, &db, &allocImp, &allocTempImp); + if (res == SZ_OK) + { + char *command = args[1]; + int listCommand = 0; + int testCommand = 0; + int extractCommand = 0; + if (strcmp(command, "l") == 0) + listCommand = 1; + if (strcmp(command, "t") == 0) + testCommand = 1; + else if (strcmp(command, "e") == 0) + extractCommand = 1; + + if (listCommand) + { + UInt32 i; + for (i = 0; i < db.Database.NumFiles; i++) + { + CFileItem *f = db.Database.Files + i; + char s[32], t[32]; + ConvertNumberToString(f->Size, s); + if (f->IsLastWriteTimeDefined) + ConvertFileTimeToString(&f->LastWriteTime, t); + else + strcpy(t, " "); + + printf("%10s %s %s\n", s, t, f->Name); + } + } + else if (testCommand || extractCommand) + { + UInt32 i; + + /* + if you need cache, use these 3 variables. + if you use external function, you can make these variable as static. + */ + UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ + Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ + size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ + + printf("\n"); + for (i = 0; i < db.Database.NumFiles; i++) + { + size_t offset; + size_t outSizeProcessed; + CFileItem *f = db.Database.Files + i; + if (f->IsDirectory) + printf("Directory "); + else + printf(testCommand ? + "Testing ": + "Extracting"); + printf(" %s", f->Name); + if (f->IsDirectory) + { + printf("\n"); + continue; + } + res = SzExtract(&archiveStream.InStream, &db, i, + &blockIndex, &outBuffer, &outBufferSize, + &offset, &outSizeProcessed, + &allocImp, &allocTempImp); + if (res != SZ_OK) + break; + if (!testCommand) + { + MY_FILE_HANDLE outputHandle; + size_t processedSize; + char *fileName = f->Name; + size_t nameLen = strlen(f->Name); + for (; nameLen > 0; nameLen--) + if (f->Name[nameLen - 1] == '/') + { + fileName = f->Name + nameLen; + break; + } + + outputHandle = + #ifdef USE_WINDOWS_FUNCTIONS + CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_READ, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (outputHandle == INVALID_HANDLE_VALUE) + #else + fopen(fileName, "wb+"); + if (outputHandle == 0) + #endif + { + PrintError("can not open output file"); + res = SZE_FAIL; + break; + } + processedSize = MyWriteFile(outputHandle, outBuffer + offset, outSizeProcessed); + if (processedSize != outSizeProcessed) + { + PrintError("can not write output file"); + res = SZE_FAIL; + break; + } + if (MyCloseFile(outputHandle)) + { + PrintError("can not close output file"); + res = SZE_FAIL; + break; + } + } + printf("\n"); + } + allocImp.Free(outBuffer); + } + else + { + PrintError("incorrect command"); + res = SZE_FAIL; + } + } + SzArDbExFree(&db, allocImp.Free); + + MyCloseFile(archiveStream.File); + if (res == SZ_OK) + { + printf("\nEverything is Ok\n"); + return 0; + } + if (res == (SZ_RESULT)SZE_NOTIMPL) + PrintError("decoder doesn't support this archive"); + else if (res == (SZ_RESULT)SZE_OUTOFMEMORY) + PrintError("can not allocate memory"); + else if (res == (SZ_RESULT)SZE_CRC_ERROR) + PrintError("CRC error"); + else + printf("\nERROR #%d\n", res); + return 1; +} diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.c b/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.c new file mode 100644 index 0000000..a7e825d --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.c @@ -0,0 +1,10 @@ +/* 7zMethodID.c */ + +#include "7zMethodID.h" + +/* +int AreMethodsEqual(CMethodID *a1, CMethodID *a2) +{ + return (*a1 == *a2) ? 1 : 0; +} +*/ diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.h b/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.h new file mode 100644 index 0000000..57f22a5 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7zMethodID.h @@ -0,0 +1,10 @@ +/* 7zMethodID.h */ + +#ifndef __7Z_METHOD_ID_H +#define __7Z_METHOD_ID_H + +#include "../../Types.h" + +typedef UInt64 CMethodID; + +#endif diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsp b/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsp new file mode 100644 index 0000000..9a040ad --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsp @@ -0,0 +1,211 @@ +# Microsoft Developer Studio Project File - Name="7z_C" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=7z_C - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "7z_C.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "7z_C.mak" CFG="7z_C - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "7z_C - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "7z_C - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "7z_C - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W4 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_LZMA_PROB32" /D "_LZMA_IN_CB" /YX /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/7zDec.exe" /opt:NOWIN98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "7z_C - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_LZMA_PROB32" /D "_LZMA_IN_CB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/7zDec.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "7z_C - Win32 Release" +# Name "7z_C - Win32 Debug" +# Begin Group "LZMA" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\Lzma\LzmaDecode.c +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Lzma\LzmaDecode.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Lzma\LzmaTypes.h +# End Source File +# End Group +# Begin Group "Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\7zCrc.c +# End Source File +# Begin Source File + +SOURCE=..\..\7zCrc.h +# End Source File +# Begin Source File + +SOURCE=..\..\Types.h +# End Source File +# End Group +# Begin Group "Branch" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchTypes.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchX86.c +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchX86.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchX86_2.c +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchX86_2.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\7zAlloc.c +# End Source File +# Begin Source File + +SOURCE=.\7zAlloc.h +# End Source File +# Begin Source File + +SOURCE=.\7zBuffer.c +# End Source File +# Begin Source File + +SOURCE=.\7zBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\7zDecode.c +# End Source File +# Begin Source File + +SOURCE=.\7zDecode.h +# End Source File +# Begin Source File + +SOURCE=.\7zExtract.c +# End Source File +# Begin Source File + +SOURCE=.\7zExtract.h +# End Source File +# Begin Source File + +SOURCE=.\7zHeader.c +# End Source File +# Begin Source File + +SOURCE=.\7zHeader.h +# End Source File +# Begin Source File + +SOURCE=.\7zIn.c +# End Source File +# Begin Source File + +SOURCE=.\7zIn.h +# End Source File +# Begin Source File + +SOURCE=.\7zItem.c +# End Source File +# Begin Source File + +SOURCE=.\7zItem.h +# End Source File +# Begin Source File + +SOURCE=.\7zMain.c +# End Source File +# Begin Source File + +SOURCE=.\7zMethodID.c +# End Source File +# Begin Source File + +SOURCE=.\7zMethodID.h +# End Source File +# End Target +# End Project diff --git a/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsw b/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsw new file mode 100644 index 0000000..6fd3962 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/7z_C.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "7z_C"=.\7z_C.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/3rdparty/physfs/lzma/C/Archive/7z/makefile b/3rdparty/physfs/lzma/C/Archive/7z/makefile new file mode 100644 index 0000000..6ea3119 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/makefile @@ -0,0 +1,74 @@ +PROG = 7zDec.exe + +!IFDEF CPU +LIBS = $(LIBS) bufferoverflowU.lib +CFLAGS = $(CFLAGS) -GS- -Zc:forScope -WX -GS- -Gy -W4 +!ENDIF + +!IFNDEF O +!IFDEF CPU +O=$(CPU) +!ELSE +O=O +!ENDIF +!ENDIF + +CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ -D_LZMA_IN_CB +CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O2 = $(CFLAGS) -O2 + +LFLAGS = $(LFLAGS) -nologo -OPT:NOWIN98 -OPT:REF + +PROGPATH = $O\$(PROG) + +COMPL_O1 = $(CPP) $(CFLAGS_O1) $** +COMPL_O2 = $(CPP) $(CFLAGS_O2) $** +COMPL = $(CPP) $(CFLAGS_O1) $** + +C_OBJS = \ + $O\7zCrc.obj \ + + +7Z_OBJS = \ + $O\7zAlloc.obj \ + $O\7zBuffer.obj \ + $O\7zDecode.obj \ + $O\7zExtract.obj \ + $O\7zHeader.obj \ + $O\7zIn.obj \ + $O\7zItem.obj \ + $O\7zMain.obj \ + $O\7zMethodID.obj \ + +OBJS = \ + $(7Z_OBJS) \ + $O\LzmaDecode.obj \ + $O\BranchX86.obj \ + $O\BranchX86_2.obj \ + $(C_OBJS) \ + +all: $(PROGPATH) + +clean: + -del /Q $(PROGPATH) $O\*.exe $O\*.dll $O\*.obj $O\*.lib $O\*.exp $O\*.res $O\*.pch + +$O: + if not exist "$O" mkdir "$O" + +$(PROGPATH): $O $(OBJS) + link $(LFLAGS) -out:$(PROGPATH) $(OBJS) $(LIBS) + + +$(7Z_OBJS): $(*B).c + $(COMPL) +$O\LzmaDecode.obj: ../../Compress/Lzma/$(*B).c + $(COMPL_O2) + +$O\BranchX86.obj: ../../Compress/Branch/$(*B).c + $(COMPL_O2) + +$O\BranchX86_2.obj: ../../Compress/Branch/$(*B).c + $(COMPL_O2) + +$(C_OBJS): ../../$(*B).c + $(COMPL_O2) diff --git a/3rdparty/physfs/lzma/C/Archive/7z/makefile.gcc b/3rdparty/physfs/lzma/C/Archive/7z/makefile.gcc new file mode 100644 index 0000000..664ac25 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Archive/7z/makefile.gcc @@ -0,0 +1,55 @@ +PROG = 7zDec +CXX = g++ +LIB = +RM = rm -f +CFLAGS = -c -O2 -Wall -D_LZMA_IN_CB + +OBJS = 7zAlloc.o 7zBuffer.o 7zCrc.o 7zDecode.o 7zExtract.o 7zHeader.o 7zIn.o 7zItem.o 7zMain.o 7zMethodID.o LzmaDecode.o BranchX86.o BranchX86_2.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CXX) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIB) + +7zAlloc.o: 7zAlloc.c + $(CXX) $(CFLAGS) 7zAlloc.c + +7zBuffer.o: 7zBuffer.c + $(CXX) $(CFLAGS) 7zBuffer.c + +7zCrc.o: ../../7zCrc.c + $(CXX) $(CFLAGS) ../../7zCrc.c + +7zDecode.o: 7zDecode.c + $(CXX) $(CFLAGS) 7zDecode.c + +7zExtract.o: 7zExtract.c + $(CXX) $(CFLAGS) 7zExtract.c + +7zHeader.o: 7zHeader.c + $(CXX) $(CFLAGS) 7zHeader.c + +7zIn.o: 7zIn.c + $(CXX) $(CFLAGS) 7zIn.c + +7zItem.o: 7zItem.c + $(CXX) $(CFLAGS) 7zItem.c + +7zMain.o: 7zMain.c + $(CXX) $(CFLAGS) 7zMain.c + +7zMethodID.o: 7zMethodID.c + $(CXX) $(CFLAGS) 7zMethodID.c + +LzmaDecode.o: ../../Compress/Lzma/LzmaDecode.c + $(CXX) $(CFLAGS) ../../Compress/Lzma/LzmaDecode.c + +BranchX86.o: ../../Compress/Branch/BranchX86.c + $(CXX) $(CFLAGS) ../../Compress/Branch/BranchX86.c + +BranchX86_2.o: ../../Compress/Branch/BranchX86_2.c + $(CXX) $(CFLAGS) ../../Compress/Branch/BranchX86_2.c + +clean: + -$(RM) $(PROG) $(OBJS) + diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.c new file mode 100644 index 0000000..da7db26 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.c @@ -0,0 +1,26 @@ +/* BranchARM.c */ + +#include "BranchARM.h" + +UInt32 ARM_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding) +{ + UInt32 i; + for (i = 0; i + 4 <= size; i += 4) + { + if (data[i + 3] == 0xEB) + { + UInt32 dest; + UInt32 src = (data[i + 2] << 16) | (data[i + 1] << 8) | (data[i + 0]); + src <<= 2; + if (encoding) + dest = nowPos + i + 8 + src; + else + dest = src - (nowPos + i + 8); + dest >>= 2; + data[i + 2] = (Byte)(dest >> 16); + data[i + 1] = (Byte)(dest >> 8); + data[i + 0] = (Byte)dest; + } + } + return i; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.h new file mode 100644 index 0000000..3b4d63e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARM.h @@ -0,0 +1,10 @@ +/* BranchARM.h */ + +#ifndef __BRANCH_ARM_H +#define __BRANCH_ARM_H + +#include "BranchTypes.h" + +UInt32 ARM_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.c new file mode 100644 index 0000000..fa30e43 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.c @@ -0,0 +1,35 @@ +/* BranchARMThumb.c */ + +#include "BranchARMThumb.h" + +UInt32 ARMThumb_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding) +{ + UInt32 i; + for (i = 0; i + 4 <= size; i += 2) + { + if ((data[i + 1] & 0xF8) == 0xF0 && + (data[i + 3] & 0xF8) == 0xF8) + { + UInt32 dest; + UInt32 src = + ((data[i + 1] & 0x7) << 19) | + (data[i + 0] << 11) | + ((data[i + 3] & 0x7) << 8) | + (data[i + 2]); + + src <<= 1; + if (encoding) + dest = nowPos + i + 4 + src; + else + dest = src - (nowPos + i + 4); + dest >>= 1; + + data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); + data[i + 0] = (Byte)(dest >> 11); + data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); + data[i + 2] = (Byte)dest; + i += 2; + } + } + return i; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.h new file mode 100644 index 0000000..304699c --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchARMThumb.h @@ -0,0 +1,10 @@ +/* BranchARMThumb.h */ + +#ifndef __BRANCH_ARM_THUMB_H +#define __BRANCH_ARM_THUMB_H + +#include "BranchTypes.h" + +UInt32 ARMThumb_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.c new file mode 100644 index 0000000..009086b --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.c @@ -0,0 +1,66 @@ +/* BranchIA64.c */ + +#include "BranchIA64.h" + +const Byte kBranchTable[32] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 +}; + +UInt32 IA64_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding) +{ + UInt32 i; + for (i = 0; i + 16 <= size; i += 16) + { + UInt32 instrTemplate = data[i] & 0x1F; + UInt32 mask = kBranchTable[instrTemplate]; + UInt32 bitPos = 5; + int slot; + for (slot = 0; slot < 3; slot++, bitPos += 41) + { + UInt32 bytePos, bitRes; + UInt64 instruction, instNorm; + int j; + if (((mask >> slot) & 1) == 0) + continue; + bytePos = (bitPos >> 3); + bitRes = bitPos & 0x7; + instruction = 0; + for (j = 0; j < 6; j++) + instruction += (UInt64)(data[i + j + bytePos]) << (8 * j); + + instNorm = instruction >> bitRes; + if (((instNorm >> 37) & 0xF) == 0x5 + && ((instNorm >> 9) & 0x7) == 0 + /* && (instNorm & 0x3F)== 0 */ + ) + { + UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); + UInt32 dest; + src |= ((UInt32)(instNorm >> 36) & 1) << 20; + + src <<= 4; + + if (encoding) + dest = nowPos + i + src; + else + dest = src - (nowPos + i); + + dest >>= 4; + + instNorm &= ~((UInt64)(0x8FFFFF) << 13); + instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); + instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); + + instruction &= (1 << bitRes) - 1; + instruction |= (instNorm << bitRes); + for (j = 0; j < 6; j++) + data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); + } + } + } + return i; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.h new file mode 100644 index 0000000..8213cb5 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchIA64.h @@ -0,0 +1,10 @@ +/* BranchIA64.h */ + +#ifndef __BRANCH_IA64_H +#define __BRANCH_IA64_H + +#include "BranchTypes.h" + +UInt32 IA64_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.c new file mode 100644 index 0000000..b3e5703 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.c @@ -0,0 +1,36 @@ +/* BranchPPC.c */ + +#include "BranchPPC.h" + +UInt32 PPC_B_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding) +{ + UInt32 i; + for (i = 0; i + 4 <= size; i += 4) + { + /* PowerPC branch 6(48) 24(Offset) 1(Abs) 1(Link) */ + if ((data[i] >> 2) == 0x12 && + ( + (data[i + 3] & 3) == 1 + /* || (data[i+3] & 3) == 3 */ + ) + ) + { + UInt32 src = ((data[i + 0] & 3) << 24) | + (data[i + 1] << 16) | + (data[i + 2] << 8) | + (data[i + 3] & (~3)); + + UInt32 dest; + if (encoding) + dest = nowPos + i + src; + else + dest = src - (nowPos + i); + data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] &= 0x3; + data[i + 3] |= dest; + } + } + return i; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.h new file mode 100644 index 0000000..05e2a37 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchPPC.h @@ -0,0 +1,10 @@ +/* BranchPPC.h */ + +#ifndef __BRANCH_PPC_H +#define __BRANCH_PPC_H + +#include "BranchTypes.h" + +UInt32 PPC_B_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.c new file mode 100644 index 0000000..79da2ea --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.c @@ -0,0 +1,36 @@ +/* BranchSPARC.c */ + +#include "BranchSPARC.h" + +UInt32 SPARC_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding) +{ + UInt32 i; + for (i = 0; i + 4 <= size; i += 4) + { + if (data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00 || + data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0) + { + UInt32 src = + ((UInt32)data[i + 0] << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3]); + UInt32 dest; + + src <<= 2; + if (encoding) + dest = nowPos + i + src; + else + dest = src - (nowPos + i); + dest >>= 2; + + dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; + + data[i + 0] = (Byte)(dest >> 24); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] = (Byte)dest; + } + } + return i; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.h new file mode 100644 index 0000000..f3f8320 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchSPARC.h @@ -0,0 +1,10 @@ +/* BranchSPARC.h */ + +#ifndef __BRANCH_SPARC_H +#define __BRANCH_SPARC_H + +#include "BranchTypes.h" + +UInt32 SPARC_Convert(Byte *data, UInt32 size, UInt32 nowPos, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchTypes.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchTypes.h new file mode 100644 index 0000000..1bed56a --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchTypes.h @@ -0,0 +1,51 @@ +/* BranchTypes.h */ + +#ifndef __BRANCHTYPES_H +#define __BRANCHTYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +#ifdef _LZMA_UINT32_IS_ULONG +typedef unsigned long UInt32; +#else +typedef unsigned int UInt32; +#endif +#endif + +#ifndef _7ZIP_UINT64_DEFINED +#define _7ZIP_UINT64_DEFINED +#ifdef _SZ_NO_INT_64 +typedef unsigned long UInt64; +#else +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 UInt64; +#else +typedef unsigned long long int UInt64; +#endif +#endif +#endif + +/* #define _LZMA_NO_SYSTEM_SIZE_T */ +/* You can use it, if you don't want */ + +#ifndef _7ZIP_SIZET_DEFINED +#define _7ZIP_SIZET_DEFINED +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +#include +typedef size_t SizeT; +#endif +#endif + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.c new file mode 100644 index 0000000..fd1d334 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.c @@ -0,0 +1,84 @@ +/* BranchX86.c */ + +#include "BranchX86.h" + +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) + +const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; +const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; + +SizeT x86_Convert(Byte *buffer, SizeT endPos, UInt32 nowPos, UInt32 *prevMaskMix, int encoding) +{ + SizeT bufferPos = 0, prevPosT; + UInt32 prevMask = *prevMaskMix & 0x7; + if (endPos < 5) + return 0; + nowPos += 5; + prevPosT = (SizeT)0 - 1; + + for(;;) + { + Byte *p = buffer + bufferPos; + Byte *limit = buffer + endPos - 4; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + bufferPos = (SizeT)(p - buffer); + if (p >= limit) + break; + prevPosT = bufferPos - prevPosT; + if (prevPosT > 3) + prevMask = 0; + else + { + prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; + if (prevMask != 0) + { + Byte b = p[4 - kMaskToBitNumber[prevMask]]; + if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + { + prevPosT = bufferPos; + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + continue; + } + } + } + prevPosT = bufferPos; + + if (Test86MSByte(p[4])) + { + UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 dest; + for (;;) + { + Byte b; + int index; + if (encoding) + dest = (nowPos + (UInt32)bufferPos) + src; + else + dest = src - (nowPos + (UInt32)bufferPos); + if (prevMask == 0) + break; + index = kMaskToBitNumber[prevMask] * 8; + b = (Byte)(dest >> (24 - index)); + if (!Test86MSByte(b)) + break; + src = dest ^ ((1 << (32 - index)) - 1); + } + p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); + p[3] = (Byte)(dest >> 16); + p[2] = (Byte)(dest >> 8); + p[1] = (Byte)dest; + bufferPos += 5; + } + else + { + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + } + } + prevPosT = bufferPos - prevPosT; + *prevMaskMix = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); + return bufferPos; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.h new file mode 100644 index 0000000..5dfd02a --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86.h @@ -0,0 +1,12 @@ +/* BranchX86.h */ + +#ifndef __BRANCHX86_H +#define __BRANCHX86_H + +#include "BranchTypes.h" + +#define x86_Convert_Init(state) { state = 0; } + +SizeT x86_Convert(Byte *buffer, SizeT endPos, UInt32 nowPos, UInt32 *state, int encoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.c b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.c new file mode 100644 index 0000000..241789a --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.c @@ -0,0 +1,135 @@ +// BranchX86_2.c + +#include "BranchX86_2.h" + +#include "../../Alloc.h" + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + +#define RC_TEST { if (Buffer == BufferLim) return BCJ2_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; +// #define UpdateBit0(p) Range = bound; *(p) = (CProb)(*(p) + ((kBitModelTotal - *(p)) >> kNumMoveBits)); +// #define UpdateBit1(p) Range -= bound; Code -= bound; *(p) = (CProb)(*(p) - (*(p) >> kNumMoveBits)); + +int x86_2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize) +{ + CProb p[256 + 2]; + SizeT inPos = 0, outPos = 0; + + const Byte *Buffer, *BufferLim; + UInt32 Range, Code; + Byte prevByte = 0; + + unsigned int i; + for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) + p[i] = kBitModelTotal >> 1; + RC_INIT(buf3, size3); + + if (outSize == 0) + return BCJ2_RESULT_OK; + + for (;;) + { + Byte b; + CProb *prob; + UInt32 bound; + + SizeT limit = size0 - inPos; + if (outSize - outPos < limit) + limit = outSize - outPos; + while (limit != 0) + { + Byte b = buf0[inPos]; + outBuf[outPos++] = b; + if (IsJ(prevByte, b)) + break; + inPos++; + prevByte = b; + limit--; + } + + if (limit == 0 || outPos == outSize) + break; + + b = buf0[inPos++]; + + if (b == 0xE8) + prob = p + prevByte; + else if (b == 0xE9) + prob = p + 256; + else + prob = p + 257; + + IfBit0(prob) + { + UpdateBit0(prob) + prevByte = b; + } + else + { + UInt32 dest; + const Byte *v; + UpdateBit1(prob) + if (b == 0xE8) + { + v = buf1; + if (size1 < 4) + return BCJ2_RESULT_DATA_ERROR; + buf1 += 4; + size1 -= 4; + } + else + { + v = buf2; + if (size2 < 4) + return BCJ2_RESULT_DATA_ERROR; + buf2 += 4; + size2 -= 4; + } + dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | + ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); + outBuf[outPos++] = (Byte)dest; + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 8); + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 16); + if (outPos == outSize) + break; + outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + } + return (outPos == outSize) ? BCJ2_RESULT_OK : BCJ2_RESULT_DATA_ERROR; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.h b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.h new file mode 100644 index 0000000..4d861fa --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Branch/BranchX86_2.h @@ -0,0 +1,28 @@ +// BranchX86_2.h + +#ifndef __BRANCHX86_2_H +#define __BRANCHX86_2_H + +#include "BranchTypes.h" + +#define BCJ2_RESULT_OK 0 +#define BCJ2_RESULT_DATA_ERROR 1 + +/* +Conditions: + outSize <= FullOutputSize, + where FullOutputSize is full size of output stream of x86_2 filter. + +If buf0 overlaps outBuf, there are two required conditions: + 1) (buf0 >= outBuf) + 2) (buf0 + size0 >= outBuf + FullOutputSize). +*/ + +int x86_2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.c b/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.c new file mode 100644 index 0000000..f27607e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.c @@ -0,0 +1,146 @@ +/* Compress/HuffmanEncode.c */ + +#include "HuffmanEncode.h" +#include "../../Sort.h" + +#define kMaxLen 16 +#define NUM_BITS 10 +#define MASK ((1 << NUM_BITS) - 1) + +#define NUM_COUNTERS 64 + +/* use BLOCK_SORT_EXTERNAL_FLAGS if blockSize > 1M */ +#define HUFFMAN_SPEED_OPT + +void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 numSymbols, UInt32 maxLen) +{ + UInt32 num = 0; + /* if (maxLen > 10) maxLen = 10; */ + { + UInt32 i; + + #ifdef HUFFMAN_SPEED_OPT + + UInt32 counters[NUM_COUNTERS]; + for (i = 0; i < NUM_COUNTERS; i++) + counters[i] = 0; + for (i = 0; i < numSymbols; i++) + { + UInt32 freq = freqs[i]; + counters[(freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1]++; + } + + for (i = 1; i < NUM_COUNTERS; i++) + { + UInt32 temp = counters[i]; + counters[i] = num; + num += temp; + } + + for (i = 0; i < numSymbols; i++) + { + UInt32 freq = freqs[i]; + if (freq == 0) + lens[i] = 0; + else + p[counters[((freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1)]++] = i | (freq << NUM_BITS); + } + counters[0] = 0; + HeapSort(p + counters[NUM_COUNTERS - 2], counters[NUM_COUNTERS - 1] - counters[NUM_COUNTERS - 2]); + + #else + + for (i = 0; i < numSymbols; i++) + { + UInt32 freq = freqs[i]; + if (freq == 0) + lens[i] = 0; + else + p[num++] = i | (freq << NUM_BITS); + } + HeapSort(p, num); + + #endif + } + + if (num < 2) + { + int minCode = 0; + int maxCode = 1; + if (num == 1) + { + maxCode = p[0] & MASK; + if (maxCode == 0) + maxCode++; + } + p[minCode] = 0; + p[maxCode] = 1; + lens[minCode] = lens[maxCode] = 1; + return; + } + + { + UInt32 b, e, i; + + i = b = e = 0; + do + { + UInt32 n, m, freq; + n = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++; + freq = (p[n] & ~MASK); + p[n] = (p[n] & MASK) | (e << NUM_BITS); + m = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++; + freq += (p[m] & ~MASK); + p[m] = (p[m] & MASK) | (e << NUM_BITS); + p[e] = (p[e] & MASK) | freq; + e++; + } + while (num - e > 1); + + { + UInt32 lenCounters[kMaxLen + 1]; + for (i = 0; i <= kMaxLen; i++) + lenCounters[i] = 0; + + p[--e] &= MASK; + lenCounters[1] = 2; + while (e > 0) + { + UInt32 len = (p[p[--e] >> NUM_BITS] >> NUM_BITS) + 1; + p[e] = (p[e] & MASK) | (len << NUM_BITS); + if (len >= maxLen) + for (len = maxLen - 1; lenCounters[len] == 0; len--); + lenCounters[len]--; + lenCounters[len + 1] += 2; + } + + { + UInt32 len; + i = 0; + for (len = maxLen; len != 0; len--) + { + UInt32 num; + for (num = lenCounters[len]; num != 0; num--) + lens[p[i++] & MASK] = (Byte)len; + } + } + + { + UInt32 nextCodes[kMaxLen + 1]; + { + UInt32 code = 0; + UInt32 len; + for (len = 1; len <= kMaxLen; len++) + nextCodes[len] = code = (code + lenCounters[len - 1]) << 1; + } + /* if (code + lenCounters[kMaxLen] - 1 != (1 << kMaxLen) - 1) throw 1; */ + + { + UInt32 i; + for (i = 0; i < numSymbols; i++) + p[i] = nextCodes[lens[i]]++; + } + } + } + } +} diff --git a/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.h b/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.h new file mode 100644 index 0000000..c8acc28 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Huffman/HuffmanEncode.h @@ -0,0 +1,18 @@ +/* Compress/HuffmanEncode.h */ + +#ifndef __COMPRESS_HUFFMANENCODE_H +#define __COMPRESS_HUFFMANENCODE_H + +#include "../../Types.h" + +/* +Conditions: + num <= 1024 = 2 ^ NUM_BITS + Sum(freqs) < 4M = 2 ^ (32 - NUM_BITS) + maxLen <= 16 = kMaxLen + Num_Items(p) >= HUFFMAN_TEMP_SIZE(num) +*/ + +void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 num, UInt32 maxLen); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lz/LzHash.h b/3rdparty/physfs/lzma/C/Compress/Lz/LzHash.h new file mode 100644 index 0000000..87c2836 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lz/LzHash.h @@ -0,0 +1,53 @@ +/* LzHash.h */ + +#ifndef __C_LZHASH_H +#define __C_LZHASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); + +#define HASH3_CALC { \ + UInt32 temp = g_CrcTable[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + UInt32 temp = g_CrcTable[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (g_CrcTable[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ + UInt32 temp = g_CrcTable[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (g_CrcTable[cur[3]] << 5)); \ + hashValue = (hash4Value ^ (g_CrcTable[cur[4]] << 3)) & p->hashMask; \ + hash4Value &= (kHash4Size - 1); } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ g_CrcTable[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ g_CrcTable[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ + hash2Value = (g_CrcTable[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ + UInt32 temp = g_CrcTable[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ + UInt32 temp = g_CrcTable[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (g_CrcTable[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.c b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.c new file mode 100644 index 0000000..0f530fc --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.c @@ -0,0 +1,742 @@ +/* MatchFinder.c */ +/* Please call InitCrcTable before */ + +#include + +#include "MatchFinder.h" +#include "LzHash.h" + +#include "../../7zCrc.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)3 << 30) + +#define kStartMaxLen 3 + +void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + if (!p->directInput) + { + alloc->Free(p->bufferBase); + p->bufferBase = 0; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +{ + UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (p->bufferBase == 0 || p->blockSize != blockSize) + { + LzInWindow_Free(p, alloc); + p->blockSize = blockSize; + p->bufferBase = (Byte *)alloc->Alloc(blockSize); + } + return (p->bufferBase != 0); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } + +UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + for (;;) + { + Byte *dest = p->buffer + (p->streamPos - p->pos); + UInt32 numReadBytes; + UInt32 size = (UInt32)(p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + p->result = p->stream->Read(p->stream, dest, size, &numReadBytes); + if (p->result != SZ_OK) + return; + if (numReadBytes == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += numReadBytes; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, + p->streamPos - p->pos + p->keepSizeBefore); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + /* p->skipModeBits = 0; */ + p->directInput = 0; + p->bigHash = 0; +} + +void MatchFinder_Construct(CMatchFinder *p) +{ + p->bufferBase = 0; + p->directInput = 0; + p->hash = 0; + MatchFinder_SetDefaultSettings(p); +} + +void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ + alloc->Free(p->hash); + p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + MatchFinder_FreeThisClassMemory(p, alloc); + LzInWindow_Free(p, alloc); +} + +CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return 0; + return (CLzRef *)alloc->Alloc(sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc) +{ + UInt32 sizeReserv; + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p, alloc); + return 0; + } + sizeReserv = historySize >> 1; + if (historySize > ((UInt32)2 << 30)) + sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + if (LzInWindow_Create(p, sizeReserv, alloc)) + { + UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1; + UInt32 hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + /* hs >>= p->skipModeBits; */ + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + UInt32 prevSize = p->hashSizeSum + p->numSons; + UInt32 newSize; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); + newSize = p->hashSizeSum + p->numSons; + if (p->hash != 0 && prevSize == newSize) + return 1; + MatchFinder_FreeThisClassMemory(p, alloc); + p->hash = AllocRefs(newSize, alloc); + if (p->hash != 0) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + MatchFinder_Free(p, alloc); + return 0; +} + +void MatchFinder_SetLimits(CMatchFinder *p) +{ + UInt32 limit = kMaxValForNormalize - p->pos; + UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + if (limit2 < limit) + limit = limit2; + { + UInt32 lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ + UInt32 i; + for(i = 0; i < p->hashSizeSum; i++) + p->hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); +} + +UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) +{ + UInt32 i; + for (i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +void MatchFinder_Normalize(CMatchFinder *p) +{ + UInt32 subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_ReduceOffsets(p, subValue); +} + +void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const Byte *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + UInt32 len = 0; + while(++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while(++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while(++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ + ++p->cyclicBufferPos; \ + p->buffer++; \ + if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ + UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ + lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ + cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ + offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ + distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, delta2, maxLen, offset; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + curMatch = p->hash[kFix3HashSize + hashValue]; + + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + + + maxLen = 2; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[0] = maxLen; + distances[1] = delta2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + GET_MATCHES_FOOTER(offset, maxLen) +} + +UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) +} + +UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} + +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances, 2) - (distances)); + MOVE_POS_RET +} + +void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value; + SKIP_HEADER(3) + HASH3_CALC; + curMatch = p->hash[kFix3HashSize + hashValue]; + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = p->pos; + p->hash[kFix4HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.h b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.h new file mode 100644 index 0000000..74f52a8 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinder.h @@ -0,0 +1,106 @@ +/* MatchFinder.h */ + +#ifndef __MATCHFINDER_H +#define __MATCHFINDER_H + +#include "../../IStream.h" + +typedef UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + UInt32 pos; + UInt32 posLimit; + UInt32 streamPos; + UInt32 lenLimit; + + UInt32 cyclicBufferPos; + UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + UInt32 hashMask; + UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + UInt32 blockSize; + UInt32 keepSizeBefore; + UInt32 keepSizeAfter; + + UInt32 numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + UInt32 historySize; + UInt32 fixedHashSize; + UInt32 hashSizeSum; + UInt32 numSons; + + HRes result; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + UInt32 *distances, UInt32 maxLen); + +/* +Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); +typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.c b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.c new file mode 100644 index 0000000..ea65a6e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.c @@ -0,0 +1,806 @@ +/* MatchFinderMt.c */ + +#ifdef _WIN32 +#define USE_ALLOCA +#endif + +#ifdef USE_ALLOCA +#ifdef _WIN32 +#include +#else +#include +#endif +#endif + +#include "../../7zCrc.h" +#include "LzHash.h" + +#include "MatchFinderMt.h" + +void MtSync_Construct(CMtSync *p) +{ + p->wasCreated = False; + p->csWasInitialized = False; + p->csWasEntered = False; + Thread_Construct(&p->thread); + Event_Construct(&p->canStart); + Event_Construct(&p->wasStarted); + Event_Construct(&p->wasStopped); + Semaphore_Construct(&p->freeSemaphore); + Semaphore_Construct(&p->filledSemaphore); +} + +void MtSync_GetNextBlock(CMtSync *p) +{ + if (p->needStart) + { + p->numProcessedBlocks = 1; + p->needStart = False; + p->stopWriting = False; + p->exit = False; + Event_Reset(&p->wasStarted); + Event_Reset(&p->wasStopped); + + Event_Set(&p->canStart); + Event_Wait(&p->wasStarted); + } + else + { + CriticalSection_Leave(&p->cs); + p->csWasEntered = False; + p->numProcessedBlocks++; + Semaphore_Release1(&p->freeSemaphore); + } + Semaphore_Wait(&p->filledSemaphore); + CriticalSection_Enter(&p->cs); + p->csWasEntered = True; +} + +/* MtSync_StopWriting must be called if Writing was started */ + +void MtSync_StopWriting(CMtSync *p) +{ + UInt32 myNumBlocks = p->numProcessedBlocks; + if (!Thread_WasCreated(&p->thread) || p->needStart) + return; + p->stopWriting = True; + if (p->csWasEntered) + { + CriticalSection_Leave(&p->cs); + p->csWasEntered = False; + } + Semaphore_Release1(&p->freeSemaphore); + + Event_Wait(&p->wasStopped); + + while (myNumBlocks++ != p->numProcessedBlocks) + { + Semaphore_Wait(&p->filledSemaphore); + Semaphore_Release1(&p->freeSemaphore); + } + p->needStart = True; +} + +void MtSync_Destruct(CMtSync *p) +{ + if (Thread_WasCreated(&p->thread)) + { + MtSync_StopWriting(p); + p->exit = True; + if (p->needStart) + Event_Set(&p->canStart); + Thread_Wait(&p->thread); + Thread_Close(&p->thread); + } + if (p->csWasInitialized) + { + CriticalSection_Delete(&p->cs); + p->csWasInitialized = False; + } + + Event_Close(&p->canStart); + Event_Close(&p->wasStarted); + Event_Close(&p->wasStopped); + Semaphore_Close(&p->freeSemaphore); + Semaphore_Close(&p->filledSemaphore); + + p->wasCreated = False; +} + +HRes MtSync_Create2(CMtSync *p, unsigned (StdCall *startAddress)(void *), void *obj, UInt32 numBlocks) +{ + if (p->wasCreated) + return SZ_OK; + + RINOK(CriticalSection_Init(&p->cs)); + p->csWasInitialized = True; + + RINOK(AutoResetEvent_CreateNotSignaled(&p->canStart)); + RINOK(AutoResetEvent_CreateNotSignaled(&p->wasStarted)); + RINOK(AutoResetEvent_CreateNotSignaled(&p->wasStopped)); + + RINOK(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks)); + RINOK(Semaphore_Create(&p->filledSemaphore, 0, numBlocks)); + + p->needStart = True; + + RINOK(Thread_Create(&p->thread, startAddress, obj)); + p->wasCreated = True; + return SZ_OK; +} + +HRes MtSync_Create(CMtSync *p, unsigned (StdCall *startAddress)(void *), void *obj, UInt32 numBlocks) +{ + HRes res = MtSync_Create2(p, startAddress, obj, numBlocks); + if (res != SZ_OK) + MtSync_Destruct(p); + return res; +} + + +void MtSync_Init(CMtSync *p) { p->needStart = True; } + +#define kMtMaxValForNormalize 0xFFFFFFFF + +#define DEF_GetHeads(name, v) \ +static void GetHeads ## name(const Byte *p, UInt32 pos, \ +UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads) { \ +for (; numHeads != 0; numHeads--) { \ +const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++; } } + +DEF_GetHeads(2, (p[0] | ((UInt32)p[1] << 8)) & hashMask) +DEF_GetHeads(3, (g_CrcTable[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask) +DEF_GetHeads(4, (g_CrcTable[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (g_CrcTable[p[3]] << 5)) & hashMask) +DEF_GetHeads(4b, (g_CrcTable[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask) +DEF_GetHeads(5, (g_CrcTable[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (g_CrcTable[p[3]] << 5) ^ (g_CrcTable[p[4]] << 3)) & hashMask) + +void HashThreadFunc(CMatchFinderMt *mt) +{ + CMtSync *p = &mt->hashSync; + for (;;) + { + UInt32 numProcessedBlocks = 0; + Event_Wait(&p->canStart); + Event_Set(&p->wasStarted); + for (;;) + { + if (p->exit) + return; + if (p->stopWriting) + { + p->numProcessedBlocks = numProcessedBlocks; + Event_Set(&p->wasStopped); + break; + } + + { + CMatchFinder *mf = mt->MatchFinder; + if (MatchFinder_NeedMove(mf)) + { + CriticalSection_Enter(&mt->btSync.cs); + CriticalSection_Enter(&mt->hashSync.cs); + { + const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf); + const Byte *afterPtr; + MatchFinder_MoveBlock(mf); + afterPtr = MatchFinder_GetPointerToCurrentPos(mf); + mt->pointerToCurPos -= beforePtr - afterPtr; + mt->buffer -= beforePtr - afterPtr; + } + CriticalSection_Leave(&mt->btSync.cs); + CriticalSection_Leave(&mt->hashSync.cs); + continue; + } + + Semaphore_Wait(&p->freeSemaphore); + + MatchFinder_ReadIfRequired(mf); + if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize)) + { + UInt32 subValue = (mf->pos - mf->historySize - 1); + MatchFinder_ReduceOffsets(mf, subValue); + MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1); + } + { + UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize; + UInt32 num = mf->streamPos - mf->pos; + heads[0] = 2; + heads[1] = num; + if (num >= mf->numHashBytes) + { + num = num - mf->numHashBytes + 1; + if (num > kMtHashBlockSize - 2) + num = kMtHashBlockSize - 2; + mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num); + heads[0] += num; + } + mf->pos += num; + mf->buffer += num; + } + } + + Semaphore_Release1(&p->filledSemaphore); + } + } +} + +void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) +{ + MtSync_GetNextBlock(&p->hashSync); + p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize; + p->hashBufPosLimit += p->hashBuf[p->hashBufPos++]; + p->hashNumAvail = p->hashBuf[p->hashBufPos++]; +} + +#define kEmptyHashValue 0 + +/* #define MFMT_GM_INLINE */ + +#ifdef MFMT_GM_INLINE + +#if _MSC_VER >= 1300 +#define NO_INLINE __declspec(noinline) __fastcall +#else +#ifdef _MSC_VER +#define NO_INLINE __fastcall +#endif +#endif + +Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes) +{ + do + { + UInt32 *distances = _distances + 1; + UInt32 curMatch = pos - *hash++; + + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + UInt32 cutValue = _cutValue; + UInt32 maxLen = _maxLen; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + break; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while(++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + break; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } + pos++; + _cyclicBufferPos++; + cur++; + { + UInt32 num = (UInt32)(distances - _distances); + *_distances = num - 1; + _distances += num; + limit -= num; + } + } + while (limit > 0 && --size != 0); + *posRes = pos; + return limit; +} + +#endif + +void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) +{ + UInt32 numProcessed = 0; + UInt32 curPos = 2; + UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2); + distances[1] = p->hashNumAvail; + while (curPos < limit) + { + if (p->hashBufPos == p->hashBufPosLimit) + { + MatchFinderMt_GetNextBlock_Hash(p); + distances[1] = numProcessed + p->hashNumAvail; + if (p->hashNumAvail >= p->numHashBytes) + continue; + for (; p->hashNumAvail != 0; p->hashNumAvail--) + distances[curPos++] = 0; + break; + } + { + UInt32 size = p->hashBufPosLimit - p->hashBufPos; + UInt32 lenLimit = p->matchMaxLen; + UInt32 pos = p->pos; + UInt32 cyclicBufferPos = p->cyclicBufferPos; + if (lenLimit >= p->hashNumAvail) + lenLimit = p->hashNumAvail; + { + UInt32 size2 = p->hashNumAvail - lenLimit + 1; + if (size2 < size) + size = size2; + size2 = p->cyclicBufferSize - cyclicBufferPos; + if (size2 < size) + size = size2; + } + #ifndef MFMT_GM_INLINE + while (curPos < limit && size-- != 0) + { + UInt32 *startDistances = distances + curPos; + UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++], + pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, + startDistances + 1, p->numHashBytes - 1) - startDistances); + *startDistances = num - 1; + curPos += num; + cyclicBufferPos++; + pos++; + p->buffer++; + } + #else + { + UInt32 posRes; + curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, + distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes); + p->hashBufPos += posRes - pos; + cyclicBufferPos += posRes - pos; + p->buffer += posRes - pos; + pos = posRes; + } + #endif + + numProcessed += pos - p->pos; + p->hashNumAvail -= pos - p->pos; + p->pos = pos; + if (cyclicBufferPos == p->cyclicBufferSize) + cyclicBufferPos = 0; + p->cyclicBufferPos = cyclicBufferPos; + } + } + distances[0] = curPos; +} + +void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) +{ + CMtSync *sync = &p->hashSync; + if (!sync->needStart) + { + CriticalSection_Enter(&sync->cs); + sync->csWasEntered = True; + } + + BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize); + + if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) + { + UInt32 subValue = p->pos - p->cyclicBufferSize; + MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2); + p->pos -= subValue; + } + + if (!sync->needStart) + { + CriticalSection_Leave(&sync->cs); + sync->csWasEntered = False; + } +} + +void BtThreadFunc(CMatchFinderMt *mt) +{ + CMtSync *p = &mt->btSync; + for (;;) + { + UInt32 blockIndex = 0; + Event_Wait(&p->canStart); + Event_Set(&p->wasStarted); + for (;;) + { + if (p->exit) + return; + if (p->stopWriting) + { + p->numProcessedBlocks = blockIndex; + MtSync_StopWriting(&mt->hashSync); + Event_Set(&p->wasStopped); + break; + } + Semaphore_Wait(&p->freeSemaphore); + BtFillBlock(mt, blockIndex++); + Semaphore_Release1(&p->filledSemaphore); + } + } +} + +void MatchFinderMt_Construct(CMatchFinderMt *p) +{ + p->hashBuf = 0; + MtSync_Construct(&p->hashSync); + MtSync_Construct(&p->btSync); +} + +void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) +{ + alloc->Free(p->hashBuf); + p->hashBuf = 0; +} + +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) +{ + MtSync_Destruct(&p->hashSync); + MtSync_Destruct(&p->btSync); + MatchFinderMt_FreeMem(p, alloc); +} + +#define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks) +#define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks) + +static unsigned StdCall HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } +static unsigned StdCall BtThreadFunc2(void *p) +{ + #ifdef USE_ALLOCA + alloca(0x180); + #endif + BtThreadFunc((CMatchFinderMt *)p); + return 0; +} + +HRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) +{ + CMatchFinder *mf = p->MatchFinder; + p->historySize = historySize; + if (kMtBtBlockSize <= matchMaxLen * 4) + return E_INVALIDARG; + if (p->hashBuf == 0) + { + p->hashBuf = (UInt32 *)alloc->Alloc((kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); + if (p->hashBuf == 0) + return SZE_OUTOFMEMORY; + p->btBuf = p->hashBuf + kHashBufferSize; + } + keepAddBufferBefore += (kHashBufferSize + kBtBufferSize); + keepAddBufferAfter += kMtHashBlockSize; + if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc)) + return SZE_OUTOFMEMORY; + + RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks)); + RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks)); + return SZ_OK; +} + +/* Call it after ReleaseStream / SetStream */ +void MatchFinderMt_Init(CMatchFinderMt *p) +{ + CMatchFinder *mf = p->MatchFinder; + p->btBufPos = p->btBufPosLimit = 0; + p->hashBufPos = p->hashBufPosLimit = 0; + MatchFinder_Init(mf); + p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf); + p->btNumAvailBytes = 0; + p->lzPos = p->historySize + 1; + + p->hash = mf->hash; + p->fixedHashSize = mf->fixedHashSize; + + p->son = mf->son; + p->matchMaxLen = mf->matchMaxLen; + p->numHashBytes = mf->numHashBytes; + p->pos = mf->pos; + p->buffer = mf->buffer; + p->cyclicBufferPos = mf->cyclicBufferPos; + p->cyclicBufferSize = mf->cyclicBufferSize; + p->cutValue = mf->cutValue; +} + +/* ReleaseStream is required to finish multithreading */ +void MatchFinderMt_ReleaseStream(CMatchFinderMt *p) +{ + MtSync_StopWriting(&p->btSync); + /* p->MatchFinder->ReleaseStream(); */ +} + +void MatchFinderMt_Normalize(CMatchFinderMt *p) +{ + MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize); + p->lzPos = p->historySize + 1; +} + +void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) +{ + UInt32 blockIndex; + MtSync_GetNextBlock(&p->btSync); + blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask); + p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize; + p->btBufPosLimit += p->btBuf[p->btBufPos++]; + p->btNumAvailBytes = p->btBuf[p->btBufPos++]; + if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize) + MatchFinderMt_Normalize(p); +} + +const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) +{ + return p->pointerToCurPos; +} + +#define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p); + +UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) +{ + GET_NEXT_BLOCK_IF_REQUIRED; + return p->btNumAvailBytes; +} + +Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index) +{ + return p->pointerToCurPos[index]; +} + +UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ + UInt32 hash2Value, curMatch2; + UInt32 *hash = p->hash; + const Byte *cur = p->pointerToCurPos; + UInt32 lzPos = p->lzPos; + MT_HASH2_CALC + + curMatch2 = hash[hash2Value]; + hash[hash2Value] = lzPos; + + if (curMatch2 >= matchMinPos) + if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) + { + *distances++ = 2; + *distances++ = lzPos - curMatch2 - 1; + } + return distances; +} + +UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, curMatch2, curMatch3; + UInt32 *hash = p->hash; + const Byte *cur = p->pointerToCurPos; + UInt32 lzPos = p->lzPos; + MT_HASH3_CALC + + curMatch2 = hash[ hash2Value]; + curMatch3 = hash[kFix3HashSize + hash3Value]; + + hash[ hash2Value] = + hash[kFix3HashSize + hash3Value] = + lzPos; + + if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) + { + distances[1] = lzPos - curMatch2 - 1; + if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) + { + distances[0] = 3; + return distances + 2; + } + distances[0] = 2; + distances += 2; + } + if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) + { + *distances++ = 3; + *distances++ = lzPos - curMatch3 - 1; + } + return distances; +} + +/* +UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4; + UInt32 *hash = p->hash; + const Byte *cur = p->pointerToCurPos; + UInt32 lzPos = p->lzPos; + MT_HASH4_CALC + + curMatch2 = hash[ hash2Value]; + curMatch3 = hash[kFix3HashSize + hash3Value]; + curMatch4 = hash[kFix4HashSize + hash4Value]; + + hash[ hash2Value] = + hash[kFix3HashSize + hash3Value] = + hash[kFix4HashSize + hash4Value] = + lzPos; + + if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) + { + distances[1] = lzPos - curMatch2 - 1; + if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) + { + distances[0] = (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; + return distances + 2; + } + distances[0] = 2; + distances += 2; + } + if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) + { + distances[1] = lzPos - curMatch3 - 1; + if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3]) + { + distances[0] = 4; + return distances + 2; + } + distances[0] = 3; + distances += 2; + } + + if (curMatch4 >= matchMinPos) + if ( + cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] && + cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3] + ) + { + *distances++ = 4; + *distances++ = lzPos - curMatch4 - 1; + } + return distances; +} +*/ + +#define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++; + +UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) +{ + const UInt32 *btBuf = p->btBuf + p->btBufPos; + UInt32 len = *btBuf++; + p->btBufPos += 1 + len; + p->btNumAvailBytes--; + { + UInt32 i; + for (i = 0; i < len; i += 2) + { + *distances++ = *btBuf++; + *distances++ = *btBuf++; + } + } + INCREASE_LZ_POS + return len; +} + +UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) +{ + const UInt32 *btBuf = p->btBuf + p->btBufPos; + UInt32 len = *btBuf++; + p->btBufPos += 1 + len; + + if (len == 0) + { + if (p->btNumAvailBytes-- >= 4) + len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances)); + } + else + { + /* Condition: there are matches in btBuf with length < p->numHashBytes */ + UInt32 *distances2; + p->btNumAvailBytes--; + distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances); + do + { + *distances2++ = *btBuf++; + *distances2++ = *btBuf++; + } + while ((len -= 2) != 0); + len = (UInt32)(distances2 - (distances)); + } + INCREASE_LZ_POS + return len; +} + +#define SKIP_HEADER2 do { GET_NEXT_BLOCK_IF_REQUIRED +#define SKIP_HEADER(n) SKIP_HEADER2 if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash; +#define SKIP_FOOTER } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while(--num != 0); + +void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) +{ + SKIP_HEADER2 { p->btNumAvailBytes--; + SKIP_FOOTER +} + +void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) +{ + SKIP_HEADER(2) + UInt32 hash2Value; + MT_HASH2_CALC + hash[hash2Value] = p->lzPos; + SKIP_FOOTER +} + +void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) +{ + SKIP_HEADER(3) + UInt32 hash2Value, hash3Value; + MT_HASH3_CALC + hash[kFix3HashSize + hash3Value] = + hash[ hash2Value] = + p->lzPos; + SKIP_FOOTER +} + +/* +void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) +{ + SKIP_HEADER(4) + UInt32 hash2Value, hash3Value, hash4Value; + MT_HASH4_CALC + hash[kFix4HashSize + hash4Value] = + hash[kFix3HashSize + hash3Value] = + hash[ hash2Value] = + p->lzPos; + SKIP_FOOTER +} +*/ + +void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinderMt_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos; + vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches; + switch(p->MatchFinder->numHashBytes) + { + case 2: + p->GetHeadsFunc = GetHeads2; + p->MixMatchesFunc = (Mf_Mix_Matches)0; + vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip; + vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches; + break; + case 3: + p->GetHeadsFunc = GetHeads3; + p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2; + vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip; + break; + default: + /* case 4: */ + p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4; + /* p->GetHeadsFunc = GetHeads4; */ + p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3; + vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip; + break; + /* + default: + p->GetHeadsFunc = GetHeads5; + p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4; + vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip; + break; + */ + } +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.h b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.h new file mode 100644 index 0000000..e718a09 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lz/MatchFinderMt.h @@ -0,0 +1,95 @@ +/* MatchFinderMt.h */ + +#ifndef __MATCHFINDERMT_H +#define __MATCHFINDERMT_H + +#include "../../Threads.h" +#include "MatchFinder.h" + +#define kMtHashBlockSize (1 << 13) +#define kMtHashNumBlocks (1 << 3) +#define kMtHashNumBlocksMask (kMtHashNumBlocks - 1) + +#define kMtBtBlockSize (1 << 14) +#define kMtBtNumBlocks (1 << 6) +#define kMtBtNumBlocksMask (kMtBtNumBlocks - 1) + +typedef struct _CMtSync +{ + Bool wasCreated; + Bool needStart; + Bool exit; + Bool stopWriting; + + CThread thread; + CAutoResetEvent canStart; + CAutoResetEvent wasStarted; + CAutoResetEvent wasStopped; + CSemaphore freeSemaphore; + CSemaphore filledSemaphore; + Bool csWasInitialized; + Bool csWasEntered; + CCriticalSection cs; + UInt32 numProcessedBlocks; +} CMtSync; + +typedef UInt32 * (*Mf_Mix_Matches)(void *p, UInt32 matchMinPos, UInt32 *distances); + +/* kMtCacheLineDummy must be >= size_of_CPU_cache_line */ +#define kMtCacheLineDummy 128 + +typedef void (*Mf_GetHeads)(const Byte *buffer, UInt32 pos, + UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads); + +typedef struct _CMatchFinderMt +{ + /* LZ */ + const Byte *pointerToCurPos; + UInt32 *btBuf; + UInt32 btBufPos; + UInt32 btBufPosLimit; + UInt32 lzPos; + UInt32 btNumAvailBytes; + + UInt32 *hash; + UInt32 fixedHashSize; + UInt32 historySize; + + Mf_Mix_Matches MixMatchesFunc; + + /* LZ + BT */ + CMtSync btSync; + Byte btDummy[kMtCacheLineDummy]; + + /* BT */ + UInt32 *hashBuf; + UInt32 hashBufPos; + UInt32 hashBufPosLimit; + UInt32 hashNumAvail; + + CLzRef *son; + UInt32 matchMaxLen; + UInt32 numHashBytes; + UInt32 pos; + Byte *buffer; + UInt32 cyclicBufferPos; + UInt32 cyclicBufferSize; /* it must be historySize + 1 */ + UInt32 cutValue; + + /* BT + Hash */ + CMtSync hashSync; + /* Byte hashDummy[kMtCacheLineDummy]; */ + + /* Hash */ + Mf_GetHeads GetHeadsFunc; + CMatchFinder *MatchFinder; +} CMatchFinderMt; + +void MatchFinderMt_Construct(CMatchFinderMt *p); +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc); +HRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); +void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); +void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.c b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.c new file mode 100644 index 0000000..cb83453 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.c @@ -0,0 +1,584 @@ +/* + LzmaDecode.c + LZMA Decoder (optimized for Speed version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaDecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + +#ifdef _LZMA_IN_CB + +#define RC_TEST { if (Buffer == BufferLim) \ + { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \ + BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }} + +#define RC_INIT Buffer = BufferLim = 0; RC_INIT2 + +#else + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + +#endif + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + #ifdef _LZMA_OUT_READ + { + int i; + propsRes->DictionarySize = 0; + for (i = 0; i < 4; i++) + propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8); + if (propsRes->DictionarySize == 0) + propsRes->DictionarySize = 1; + } + #endif + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + + #ifdef _LZMA_OUT_READ + + UInt32 Range = vs->Range; + UInt32 Code = vs->Code; + #ifdef _LZMA_IN_CB + const Byte *Buffer = vs->Buffer; + const Byte *BufferLim = vs->BufferLim; + #else + const Byte *Buffer = inStream; + const Byte *BufferLim = inStream + inSize; + #endif + int state = vs->State; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + UInt32 distanceLimit = vs->DistanceLimit; + + Byte *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->Properties.DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + Byte tempDictionary[4]; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + if (len == kLzmaStreamWasFinishedId) + return LZMA_RESULT_OK; + + if (dictionarySize == 0) + { + dictionary = tempDictionary; + dictionarySize = 1; + tempDictionary[0] = vs->TempDictionary[0]; + } + + if (len == kLzmaNeedInitId) + { + { + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + UInt32 i; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + rep0 = rep1 = rep2 = rep3 = 1; + state = 0; + globalPos = 0; + distanceLimit = 0; + dictionaryPos = 0; + dictionary[dictionarySize - 1] = 0; + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + } + len = 0; + } + while(len != 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; + + #else /* if !_LZMA_OUT_READ */ + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + + #endif /* _LZMA_OUT_READ */ + + while(nowPos < outSize) + { + CProb *prob; + UInt32 bound; + int posState = (int)( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + #else + matchByte = outStream[nowPos - rep0]; + #endif + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (Byte)symbol; + + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #endif + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + #ifdef _LZMA_OUT_READ + UInt32 pos; + #endif + UpdateBit0(prob); + + #ifdef _LZMA_OUT_READ + if (distanceLimit == 0) + #else + if (nowPos == 0) + #endif + return LZMA_RESULT_DATA_ERROR; + + state = state < kNumLitStates ? 9 : 11; + #ifdef _LZMA_OUT_READ + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + #endif + + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + #ifdef _LZMA_OUT_READ + if (rep0 > distanceLimit) + #else + if (rep0 > nowPos) + #endif + return LZMA_RESULT_DATA_ERROR; + + #ifdef _LZMA_OUT_READ + if (dictionarySize - distanceLimit > (UInt32)len) + distanceLimit += len; + else + distanceLimit = dictionarySize; + #endif + + do + { + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + RC_NORMALIZE; + + #ifdef _LZMA_OUT_READ + vs->Range = Range; + vs->Code = Code; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = globalPos + (UInt32)nowPos; + vs->DistanceLimit = distanceLimit; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->RemainLen = len; + vs->TempDictionary[0] = tempDictionary[0]; + #endif + + #ifdef _LZMA_IN_CB + vs->Buffer = Buffer; + vs->BufferLim = BufferLim; + #else + *inSizeProcessed = (SizeT)(Buffer - inStream); + #endif + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.h b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.h new file mode 100644 index 0000000..2870eeb --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecode.h @@ -0,0 +1,113 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +#include "LzmaTypes.h" + +/* #define _LZMA_IN_CB */ +/* Use callback for input data */ + +/* #define _LZMA_OUT_READ */ +/* Use read function for output data */ + +/* #define _LZMA_PROB32 */ +/* It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case */ + +/* #define _LZMA_LOC_OPT */ +/* Enable local speed optimizations inside code */ + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + +#ifdef _LZMA_IN_CB +typedef struct _ILzmaInCallback +{ + int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize); +} ILzmaInCallback; +#endif + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; + #ifdef _LZMA_OUT_READ + UInt32 DictionarySize; + #endif +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + + #ifdef _LZMA_IN_CB + const unsigned char *Buffer; + const unsigned char *BufferLim; + #endif + + #ifdef _LZMA_OUT_READ + unsigned char *Dictionary; + UInt32 Range; + UInt32 Code; + UInt32 DictionaryPos; + UInt32 GlobalPos; + UInt32 DistanceLimit; + UInt32 Reps[4]; + int State; + int RemainLen; + unsigned char TempDictionary[4]; + #endif +} CLzmaDecoderState; + +#ifdef _LZMA_OUT_READ +#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; } +#endif + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecodeSize.c b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecodeSize.c new file mode 100644 index 0000000..a3a5eb9 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaDecodeSize.c @@ -0,0 +1,712 @@ +/* + LzmaDecodeSize.c + LZMA Decoder (optimized for Size version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaDecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +typedef struct _CRangeDecoder +{ + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback; + int Result; + #endif + int ExtraBytes; +} CRangeDecoder; + +Byte RangeDecoderReadByte(CRangeDecoder *rd) +{ + if (rd->Buffer == rd->BufferLim) + { + #ifdef _LZMA_IN_CB + SizeT size; + rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); + rd->BufferLim = rd->Buffer + size; + if (size == 0) + #endif + { + rd->ExtraBytes = 1; + return 0xFF; + } + } + return (*rd->Buffer++); +} + +/* #define ReadByte (*rd->Buffer++) */ +#define ReadByte (RangeDecoderReadByte(rd)) + +void RangeDecoderInit(CRangeDecoder *rd + #ifndef _LZMA_IN_CB + , const Byte *stream, SizeT bufferSize + #endif + ) +{ + int i; + #ifdef _LZMA_IN_CB + rd->Buffer = rd->BufferLim = 0; + #else + rd->Buffer = stream; + rd->BufferLim = stream + bufferSize; + #endif + rd->ExtraBytes = 0; + rd->Code = 0; + rd->Range = (0xFFFFFFFF); + for(i = 0; i < 5; i++) + rd->Code = (rd->Code << 8) | ReadByte; +} + +#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; +#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; +#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } + +UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) +{ + RC_INIT_VAR + UInt32 result = 0; + int i; + for (i = numTotalBits; i != 0; i--) + { + /* UInt32 t; */ + range >>= 1; + + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + /* + t = (code - range) >> 31; + t &= 1; + code -= range & (t - 1); + result = (result + result) | (1 - t); + */ + RC_NORMALIZE + } + RC_FLUSH_VAR + return result; +} + +int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) +{ + UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; + if (rd->Code < bound) + { + rd->Range = bound; + *prob += (kBitModelTotal - *prob) >> kNumMoveBits; + if (rd->Range < kTopValue) + { + rd->Code = (rd->Code << 8) | ReadByte; + rd->Range <<= 8; + } + return 0; + } + else + { + rd->Range -= bound; + rd->Code -= bound; + *prob -= (*prob) >> kNumMoveBits; + if (rd->Range < kTopValue) + { + rd->Code = (rd->Code << 8) | ReadByte; + rd->Range <<= 8; + } + return 1; + } +} + +#define RC_GET_BIT2(prob, mi, A0, A1) \ + UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ + if (code < bound) \ + { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ + else \ + { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ + RC_NORMALIZE + +#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) + +int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) +{ + int mi = 1; + int i; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + for(i = numLevels; i != 0; i--) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + mi; + RC_GET_BIT(prob, mi) + #else + mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); + #endif + } + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return mi - (1 << numLevels); +} + +int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) +{ + int mi = 1; + int i; + int symbol = 0; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + for(i = 0; i < numLevels; i++) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + mi; + RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) + #else + int bit = RangeDecoderBitDecode(probs + mi, rd); + mi = mi + mi + bit; + symbol |= (bit << i); + #endif + } + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) +{ + int symbol = 1; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + do + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + symbol; + RC_GET_BIT(prob, symbol) + #else + symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); + #endif + } + while (symbol < 0x100); + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) +{ + int symbol = 1; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + do + { + int bit; + int matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + #ifdef _LZMA_LOC_OPT + { + CProb *prob = probs + 0x100 + (matchBit << 8) + symbol; + RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) + } + #else + bit = RangeDecoderBitDecode(probs + 0x100 + (matchBit << 8) + symbol, rd); + symbol = (symbol << 1) | bit; + #endif + if (matchBit != bit) + { + while (symbol < 0x100) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + symbol; + RC_GET_BIT(prob, symbol) + #else + symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); + #endif + } + break; + } + } + while (symbol < 0x100); + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + +int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) +{ + if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) + return RangeDecoderBitTreeDecode(p + LenLow + + (posState << kLenNumLowBits), kLenNumLowBits, rd); + if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) + return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + + (posState << kLenNumMidBits), kLenNumMidBits, rd); + return kLenNumLowSymbols + kLenNumMidSymbols + + RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); +} + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + #ifdef _LZMA_OUT_READ + { + int i; + propsRes->DictionarySize = 0; + for (i = 0; i < 4; i++) + propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8); + if (propsRes->DictionarySize == 0) + propsRes->DictionarySize = 1; + } + #endif + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + CRangeDecoder rd; + + #ifdef _LZMA_OUT_READ + + int state = vs->State; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + UInt32 distanceLimit = vs->DistanceLimit; + + Byte *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->Properties.DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + Byte tempDictionary[4]; + + rd.Range = vs->Range; + rd.Code = vs->Code; + #ifdef _LZMA_IN_CB + rd.InCallback = InCallback; + rd.Buffer = vs->Buffer; + rd.BufferLim = vs->BufferLim; + #else + rd.Buffer = inStream; + rd.BufferLim = inStream + inSize; + #endif + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + if (len == kLzmaStreamWasFinishedId) + return LZMA_RESULT_OK; + + if (dictionarySize == 0) + { + dictionary = tempDictionary; + dictionarySize = 1; + tempDictionary[0] = vs->TempDictionary[0]; + } + + if (len == kLzmaNeedInitId) + { + { + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + UInt32 i; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + rep0 = rep1 = rep2 = rep3 = 1; + state = 0; + globalPos = 0; + distanceLimit = 0; + dictionaryPos = 0; + dictionary[dictionarySize - 1] = 0; + RangeDecoderInit(&rd + #ifndef _LZMA_IN_CB + , inStream, inSize + #endif + ); + #ifdef _LZMA_IN_CB + if (rd.Result != LZMA_RESULT_OK) + return rd.Result; + #endif + if (rd.ExtraBytes != 0) + return LZMA_RESULT_DATA_ERROR; + } + len = 0; + } + while(len != 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; + + #ifdef _LZMA_IN_CB + rd.Result = LZMA_RESULT_OK; + #endif + rd.ExtraBytes = 0; + + #else /* if !_LZMA_OUT_READ */ + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + #ifdef _LZMA_IN_CB + rd.InCallback = InCallback; + #endif + RangeDecoderInit(&rd + #ifndef _LZMA_IN_CB + , inStream, inSize + #endif + ); + + #ifdef _LZMA_IN_CB + if (rd.Result != LZMA_RESULT_OK) + return rd.Result; + #endif + if (rd.ExtraBytes != 0) + return LZMA_RESULT_DATA_ERROR; + + #endif /* _LZMA_OUT_READ */ + + + while(nowPos < outSize) + { + int posState = (int)( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & posStateMask); + #ifdef _LZMA_IN_CB + if (rd.Result != LZMA_RESULT_OK) + return rd.Result; + #endif + if (rd.ExtraBytes != 0) + return LZMA_RESULT_DATA_ERROR; + if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) + { + CProb *probs = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + Byte matchByte; + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + #else + matchByte = outStream[nowPos - rep0]; + #endif + previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); + } + else + previousByte = LzmaLiteralDecode(probs, &rd); + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #endif + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) + { + if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) + { + if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) + { + #ifdef _LZMA_OUT_READ + UInt32 pos; + #endif + + #ifdef _LZMA_OUT_READ + if (distanceLimit == 0) + #else + if (nowPos == 0) + #endif + return LZMA_RESULT_DATA_ERROR; + + state = state < 7 ? 9 : 11; + #ifdef _LZMA_OUT_READ + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + #endif + continue; + } + } + else + { + UInt32 distance; + if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) + distance = rep1; + else + { + if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = LzmaLenDecode(p + RepLenCoder, &rd, posState); + state = state < 7 ? 8 : 11; + } + else + { + int posSlot; + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < 7 ? 7 : 10; + len = LzmaLenDecode(p + LenCoder, &rd, posState); + posSlot = RangeDecoderBitTreeDecode(p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits), kNumPosSlotBits, &rd); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); + if (posSlot < kEndPosModelIndex) + { + rep0 += RangeDecoderReverseBitTreeDecode( + p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); + } + else + { + rep0 += RangeDecoderDecodeDirectBits(&rd, + numDirectBits - kNumAlignBits) << kNumAlignBits; + rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + #ifdef _LZMA_OUT_READ + if (rep0 > distanceLimit) + #else + if (rep0 > nowPos) + #endif + return LZMA_RESULT_DATA_ERROR; + + #ifdef _LZMA_OUT_READ + if (dictionarySize - distanceLimit > (UInt32)len) + distanceLimit += len; + else + distanceLimit = dictionarySize; + #endif + + do + { + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + + + #ifdef _LZMA_OUT_READ + vs->Range = rd.Range; + vs->Code = rd.Code; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = globalPos + (UInt32)nowPos; + vs->DistanceLimit = distanceLimit; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->RemainLen = len; + vs->TempDictionary[0] = tempDictionary[0]; + #endif + + #ifdef _LZMA_IN_CB + vs->Buffer = rd.Buffer; + vs->BufferLim = rd.BufferLim; + #else + *inSizeProcessed = (SizeT)(rd.Buffer - inStream); + #endif + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.c b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.c new file mode 100644 index 0000000..5dc8f0e --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.c @@ -0,0 +1,521 @@ +/* + LzmaStateDecode.c + LZMA Decoder (State version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaStateDecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { Code = (Code << 8) | RC_READ_BYTE; }} + +#define RC_NORMALIZE if (Range < kTopValue) { Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +/* kRequiredInBufferSize = number of required input bytes for worst case: + longest match with longest distance. + kLzmaInBufferSize must be larger than kRequiredInBufferSize + 23 bits = 2 (match select) + 10 (len) + 6 (distance) + 4(align) + 1 (RC_NORMALIZE) +*/ + +#define kRequiredInBufferSize ((23 * (kNumBitModelTotalBits - kNumMoveBits + 1) + 26 + 9) / 8) + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + { + int i; + propsRes->DictionarySize = 0; + for (i = 0; i < 4; i++) + propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8); + if (propsRes->DictionarySize == 0) + propsRes->DictionarySize = 1; + return LZMA_RESULT_OK; + } +} + +int LzmaDecode( + CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed, + int finishDecoding) +{ + UInt32 Range = vs->Range; + UInt32 Code = vs->Code; + + unsigned char *Buffer = vs->Buffer; + int BufferSize = vs->BufferSize; /* don't change it to unsigned int */ + CProb *p = vs->Probs; + + int state = vs->State; + unsigned char previousByte; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + SizeT nowPos = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + UInt32 distanceLimit = vs->DistanceLimit; + + unsigned char *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->Properties.DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + unsigned char tempDictionary[4]; + + (*inSizeProcessed) = 0; + (*outSizeProcessed) = 0; + if (len == kLzmaStreamWasFinishedId) + return LZMA_RESULT_OK; + + if (dictionarySize == 0) + { + dictionary = tempDictionary; + dictionarySize = 1; + tempDictionary[0] = vs->TempDictionary[0]; + } + + if (len == kLzmaNeedInitId) + { + while (inSize > 0 && BufferSize < kLzmaInBufferSize) + { + Buffer[BufferSize++] = *inStream++; + (*inSizeProcessed)++; + inSize--; + } + if (BufferSize < 5) + { + vs->BufferSize = BufferSize; + return finishDecoding ? LZMA_RESULT_DATA_ERROR : LZMA_RESULT_OK; + } + { + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + UInt32 i; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + rep0 = rep1 = rep2 = rep3 = 1; + state = 0; + globalPos = 0; + distanceLimit = 0; + dictionaryPos = 0; + dictionary[dictionarySize - 1] = 0; + RC_INIT; + } + len = 0; + } + while(len != 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; + + for (;;) + { + int bufferPos = (int)(Buffer - vs->Buffer); + if (BufferSize - bufferPos < kRequiredInBufferSize) + { + int i; + BufferSize -= bufferPos; + if (BufferSize < 0) + return LZMA_RESULT_DATA_ERROR; + for (i = 0; i < BufferSize; i++) + vs->Buffer[i] = Buffer[i]; + Buffer = vs->Buffer; + while (inSize > 0 && BufferSize < kLzmaInBufferSize) + { + Buffer[BufferSize++] = *inStream++; + (*inSizeProcessed)++; + inSize--; + } + if (BufferSize < kRequiredInBufferSize && !finishDecoding) + break; + } + if (nowPos >= outSize) + break; + { + CProb *prob; + UInt32 bound; + int posState = (int)((nowPos + globalPos) & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((((nowPos + globalPos)& literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (unsigned char)symbol; + + outStream[nowPos++] = previousByte; + if (distanceLimit < dictionarySize) + distanceLimit++; + + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + UInt32 pos; + UpdateBit0(prob); + if (distanceLimit == 0) + return LZMA_RESULT_DATA_ERROR; + if (distanceLimit < dictionarySize) + distanceLimit++; + state = state < kNumLitStates ? 9 : 11; + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + outStream[nowPos++] = previousByte; + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + if (rep0 > distanceLimit) + return LZMA_RESULT_DATA_ERROR; + if (dictionarySize - distanceLimit > (UInt32)len) + distanceLimit += len; + else + distanceLimit = dictionarySize; + + do + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + } + RC_NORMALIZE; + + BufferSize -= (int)(Buffer - vs->Buffer); + if (BufferSize < 0) + return LZMA_RESULT_DATA_ERROR; + { + int i; + for (i = 0; i < BufferSize; i++) + vs->Buffer[i] = Buffer[i]; + } + vs->BufferSize = BufferSize; + vs->Range = Range; + vs->Code = Code; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = (UInt32)(globalPos + nowPos); + vs->DistanceLimit = distanceLimit; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->RemainLen = len; + vs->TempDictionary[0] = tempDictionary[0]; + + (*outSizeProcessed) = nowPos; + return LZMA_RESULT_OK; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.h b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.h new file mode 100644 index 0000000..26490d6 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateDecode.h @@ -0,0 +1,96 @@ +/* + LzmaStateDecode.h + LZMA Decoder interface (State version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMASTATEDECODE_H +#define __LZMASTATEDECODE_H + +#include "LzmaTypes.h" + +/* #define _LZMA_PROB32 */ +/* It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; + UInt32 DictionarySize; +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(lzmaProps) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((lzmaProps)->lc + (lzmaProps)->lp))) + +#define kLzmaInBufferSize 64 /* don't change it. it must be larger than kRequiredInBufferSize */ + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + unsigned char *Dictionary; + + unsigned char Buffer[kLzmaInBufferSize]; + int BufferSize; + + UInt32 Range; + UInt32 Code; + UInt32 DictionaryPos; + UInt32 GlobalPos; + UInt32 DistanceLimit; + UInt32 Reps[4]; + int State; + int RemainLen; /* -2: decoder needs internal initialization + -1: stream was finished, + 0: ok + > 0: need to write RemainLen bytes as match Reps[0], + */ + unsigned char TempDictionary[4]; /* it's required when DictionarySize = 0 */ +} CLzmaDecoderState; + +#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; (vs)->BufferSize = 0; } + +/* LzmaDecode: decoding from input stream to output stream. + If finishDecoding != 0, then there are no more bytes in input stream + after inStream[inSize - 1]. */ + +int LzmaDecode(CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed, + int finishDecoding); + +#endif diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateTest.c b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateTest.c new file mode 100644 index 0000000..5df4e43 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaStateTest.c @@ -0,0 +1,195 @@ +/* +LzmaStateTest.c +Test application for LZMA Decoder (State version) + +This file written and distributed to public domain by Igor Pavlov. +This file is part of LZMA SDK 4.26 (2005-08-02) +*/ + +#include +#include +#include + +#include "LzmaStateDecode.h" + +const char *kCantReadMessage = "Can not read input file"; +const char *kCantWriteMessage = "Can not write output file"; +const char *kCantAllocateMessage = "Can not allocate memory"; + +#define kInBufferSize (1 << 15) +#define kOutBufferSize (1 << 15) + +unsigned char g_InBuffer[kInBufferSize]; +unsigned char g_OutBuffer[kOutBufferSize]; + +size_t MyReadFile(FILE *file, void *data, size_t size) + { return fread(data, 1, size, file); } + +int MyReadFileAndCheck(FILE *file, void *data, size_t size) + { return (MyReadFile(file, data, size) == size); } + +int PrintError(char *buffer, const char *message) +{ + sprintf(buffer + strlen(buffer), "\nError: "); + sprintf(buffer + strlen(buffer), message); + return 1; +} + +int main3(FILE *inFile, FILE *outFile, char *rs) +{ + /* We use two 32-bit integers to construct 64-bit integer for file size. + You can remove outSizeHigh, if you don't need >= 4GB supporting, + or you can use UInt64 outSize, if your compiler supports 64-bit integers*/ + UInt32 outSize = 0; + UInt32 outSizeHigh = 0; + + int waitEOS = 1; + /* waitEOS = 1, if there is no uncompressed size in headers, + so decoder will wait EOS (End of Stream Marker) in compressed stream */ + + int i; + int res = 0; + CLzmaDecoderState state; /* it's about 140 bytes structure, if int is 32-bit */ + unsigned char properties[LZMA_PROPERTIES_SIZE]; + SizeT inAvail = 0; + unsigned char *inBuffer = 0; + + if (sizeof(UInt32) < 4) + return PrintError(rs, "LZMA decoder needs correct UInt32"); + + /* Read LZMA properties for compressed stream */ + + if (!MyReadFileAndCheck(inFile, properties, sizeof(properties))) + return PrintError(rs, kCantReadMessage); + + /* Read uncompressed size */ + + for (i = 0; i < 8; i++) + { + unsigned char b; + if (!MyReadFileAndCheck(inFile, &b, 1)) + return PrintError(rs, kCantReadMessage); + if (b != 0xFF) + waitEOS = 0; + if (i < 4) + outSize += (UInt32)(b) << (i * 8); + else + outSizeHigh += (UInt32)(b) << ((i - 4) * 8); + } + + /* Decode LZMA properties and allocate memory */ + + if (LzmaDecodeProperties(&state.Properties, properties, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK) + return PrintError(rs, "Incorrect stream properties"); + state.Probs = (CProb *)malloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (state.Probs == 0) + return PrintError(rs, kCantAllocateMessage); + + if (state.Properties.DictionarySize == 0) + state.Dictionary = 0; + else + { + state.Dictionary = (unsigned char *)malloc(state.Properties.DictionarySize); + if (state.Dictionary == 0) + { + free(state.Probs); + return PrintError(rs, kCantAllocateMessage); + } + } + + /* Decompress */ + + LzmaDecoderInit(&state); + + do + { + SizeT inProcessed, outProcessed; + int finishDecoding; + UInt32 outAvail = kOutBufferSize; + if (!waitEOS && outSizeHigh == 0 && outAvail > outSize) + outAvail = outSize; + if (inAvail == 0) + { + inAvail = (SizeT)MyReadFile(inFile, g_InBuffer, kInBufferSize); + inBuffer = g_InBuffer; + } + finishDecoding = (inAvail == 0); + res = LzmaDecode(&state, + inBuffer, inAvail, &inProcessed, + g_OutBuffer, outAvail, &outProcessed, + finishDecoding); + if (res != 0) + { + sprintf(rs + strlen(rs), "\nDecoding error = %d\n", res); + res = 1; + break; + } + inAvail -= inProcessed; + inBuffer += inProcessed; + + if (outFile != 0) + if (fwrite(g_OutBuffer, 1, outProcessed, outFile) != outProcessed) + { + PrintError(rs, kCantWriteMessage); + res = 1; + break; + } + + if (outSize < outProcessed) + outSizeHigh--; + outSize -= (UInt32)outProcessed; + outSize &= 0xFFFFFFFF; + + if (outProcessed == 0 && finishDecoding) + { + if (!waitEOS && (outSize != 0 || outSizeHigh != 0)) + res = 1; + break; + } + } + while ((outSize != 0 && outSizeHigh == 0) || outSizeHigh != 0 || waitEOS); + + free(state.Dictionary); + free(state.Probs); + return res; +} + +int main2(int numArgs, const char *args[], char *rs) +{ + FILE *inFile = 0; + FILE *outFile = 0; + int res; + + sprintf(rs + strlen(rs), "\nLZMA Decoder 4.26 Copyright (c) 1999-2005 Igor Pavlov 2005-08-02\n"); + if (numArgs < 2 || numArgs > 3) + { + sprintf(rs + strlen(rs), "\nUsage: lzmadec file.lzma [outFile]\n"); + return 1; + } + + inFile = fopen(args[1], "rb"); + if (inFile == 0) + return PrintError(rs, "Can not open input file"); + + if (numArgs > 2) + { + outFile = fopen(args[2], "wb+"); + if (outFile == 0) + return PrintError(rs, "Can not open output file"); + } + + res = main3(inFile, outFile, rs); + + if (outFile != 0) + fclose(outFile); + fclose(inFile); + return res; +} + +int main(int numArgs, const char *args[]) +{ + char rs[800] = { 0 }; + int res = main2(numArgs, args, rs); + printf(rs); + return res; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTest.c b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTest.c new file mode 100644 index 0000000..f95a753 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTest.c @@ -0,0 +1,342 @@ +/* +LzmaTest.c +Test application for LZMA Decoder + +This file written and distributed to public domain by Igor Pavlov. +This file is part of LZMA SDK 4.26 (2005-08-05) +*/ + +#include +#include +#include + +#include "LzmaDecode.h" + +const char *kCantReadMessage = "Can not read input file"; +const char *kCantWriteMessage = "Can not write output file"; +const char *kCantAllocateMessage = "Can not allocate memory"; + +size_t MyReadFile(FILE *file, void *data, size_t size) +{ + if (size == 0) + return 0; + return fread(data, 1, size, file); +} + +int MyReadFileAndCheck(FILE *file, void *data, size_t size) + { return (MyReadFile(file, data, size) == size);} + +size_t MyWriteFile(FILE *file, const void *data, size_t size) +{ + if (size == 0) + return 0; + return fwrite(data, 1, size, file); +} + +int MyWriteFileAndCheck(FILE *file, const void *data, size_t size) + { return (MyWriteFile(file, data, size) == size); } + +#ifdef _LZMA_IN_CB +#define kInBufferSize (1 << 15) +typedef struct _CBuffer +{ + ILzmaInCallback InCallback; + FILE *File; + unsigned char Buffer[kInBufferSize]; +} CBuffer; + +int LzmaReadCompressed(void *object, const unsigned char **buffer, SizeT *size) +{ + CBuffer *b = (CBuffer *)object; + *buffer = b->Buffer; + *size = (SizeT)MyReadFile(b->File, b->Buffer, kInBufferSize); + return LZMA_RESULT_OK; +} +CBuffer g_InBuffer; + +#endif + +#ifdef _LZMA_OUT_READ +#define kOutBufferSize (1 << 15) +unsigned char g_OutBuffer[kOutBufferSize]; +#endif + +int PrintError(char *buffer, const char *message) +{ + sprintf(buffer + strlen(buffer), "\nError: "); + sprintf(buffer + strlen(buffer), message); + return 1; +} + +int main3(FILE *inFile, FILE *outFile, char *rs) +{ + /* We use two 32-bit integers to construct 64-bit integer for file size. + You can remove outSizeHigh, if you don't need >= 4GB supporting, + or you can use UInt64 outSize, if your compiler supports 64-bit integers*/ + UInt32 outSize = 0; + UInt32 outSizeHigh = 0; + #ifndef _LZMA_OUT_READ + SizeT outSizeFull; + unsigned char *outStream; + #endif + + int waitEOS = 1; + /* waitEOS = 1, if there is no uncompressed size in headers, + so decoder will wait EOS (End of Stream Marker) in compressed stream */ + + #ifndef _LZMA_IN_CB + SizeT compressedSize; + unsigned char *inStream; + #endif + + CLzmaDecoderState state; /* it's about 24-80 bytes structure, if int is 32-bit */ + unsigned char properties[LZMA_PROPERTIES_SIZE]; + + int res; + + #ifdef _LZMA_IN_CB + g_InBuffer.File = inFile; + #endif + + if (sizeof(UInt32) < 4) + return PrintError(rs, "LZMA decoder needs correct UInt32"); + + #ifndef _LZMA_IN_CB + { + long length; + fseek(inFile, 0, SEEK_END); + length = ftell(inFile); + fseek(inFile, 0, SEEK_SET); + if ((long)(SizeT)length != length) + return PrintError(rs, "Too big compressed stream"); + compressedSize = (SizeT)(length - (LZMA_PROPERTIES_SIZE + 8)); + } + #endif + + /* Read LZMA properties for compressed stream */ + + if (!MyReadFileAndCheck(inFile, properties, sizeof(properties))) + return PrintError(rs, kCantReadMessage); + + /* Read uncompressed size */ + + { + int i; + for (i = 0; i < 8; i++) + { + unsigned char b; + if (!MyReadFileAndCheck(inFile, &b, 1)) + return PrintError(rs, kCantReadMessage); + if (b != 0xFF) + waitEOS = 0; + if (i < 4) + outSize += (UInt32)(b) << (i * 8); + else + outSizeHigh += (UInt32)(b) << ((i - 4) * 8); + } + + #ifndef _LZMA_OUT_READ + if (waitEOS) + return PrintError(rs, "Stream with EOS marker is not supported"); + outSizeFull = (SizeT)outSize; + if (sizeof(SizeT) >= 8) + outSizeFull |= (((SizeT)outSizeHigh << 16) << 16); + else if (outSizeHigh != 0 || (UInt32)(SizeT)outSize != outSize) + return PrintError(rs, "Too big uncompressed stream"); + #endif + } + + /* Decode LZMA properties and allocate memory */ + + if (LzmaDecodeProperties(&state.Properties, properties, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK) + return PrintError(rs, "Incorrect stream properties"); + state.Probs = (CProb *)malloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + + #ifdef _LZMA_OUT_READ + if (state.Properties.DictionarySize == 0) + state.Dictionary = 0; + else + state.Dictionary = (unsigned char *)malloc(state.Properties.DictionarySize); + #else + if (outSizeFull == 0) + outStream = 0; + else + outStream = (unsigned char *)malloc(outSizeFull); + #endif + + #ifndef _LZMA_IN_CB + if (compressedSize == 0) + inStream = 0; + else + inStream = (unsigned char *)malloc(compressedSize); + #endif + + if (state.Probs == 0 + #ifdef _LZMA_OUT_READ + || (state.Dictionary == 0 && state.Properties.DictionarySize != 0) + #else + || (outStream == 0 && outSizeFull != 0) + #endif + #ifndef _LZMA_IN_CB + || (inStream == 0 && compressedSize != 0) + #endif + ) + { + free(state.Probs); + #ifdef _LZMA_OUT_READ + free(state.Dictionary); + #else + free(outStream); + #endif + #ifndef _LZMA_IN_CB + free(inStream); + #endif + return PrintError(rs, kCantAllocateMessage); + } + + /* Decompress */ + + #ifdef _LZMA_IN_CB + g_InBuffer.InCallback.Read = LzmaReadCompressed; + #else + if (!MyReadFileAndCheck(inFile, inStream, compressedSize)) + return PrintError(rs, kCantReadMessage); + #endif + + #ifdef _LZMA_OUT_READ + { + #ifndef _LZMA_IN_CB + SizeT inAvail = compressedSize; + const unsigned char *inBuffer = inStream; + #endif + LzmaDecoderInit(&state); + do + { + #ifndef _LZMA_IN_CB + SizeT inProcessed; + #endif + SizeT outProcessed; + SizeT outAvail = kOutBufferSize; + if (!waitEOS && outSizeHigh == 0 && outAvail > outSize) + outAvail = (SizeT)outSize; + res = LzmaDecode(&state, + #ifdef _LZMA_IN_CB + &g_InBuffer.InCallback, + #else + inBuffer, inAvail, &inProcessed, + #endif + g_OutBuffer, outAvail, &outProcessed); + if (res != 0) + { + sprintf(rs + strlen(rs), "\nDecoding error = %d\n", res); + res = 1; + break; + } + #ifndef _LZMA_IN_CB + inAvail -= inProcessed; + inBuffer += inProcessed; + #endif + + if (outFile != 0) + if (!MyWriteFileAndCheck(outFile, g_OutBuffer, (size_t)outProcessed)) + { + PrintError(rs, kCantWriteMessage); + res = 1; + break; + } + + if (outSize < outProcessed) + outSizeHigh--; + outSize -= (UInt32)outProcessed; + outSize &= 0xFFFFFFFF; + + if (outProcessed == 0) + { + if (!waitEOS && (outSize != 0 || outSizeHigh != 0)) + res = 1; + break; + } + } + while ((outSize != 0 && outSizeHigh == 0) || outSizeHigh != 0 || waitEOS); + } + + #else + { + #ifndef _LZMA_IN_CB + SizeT inProcessed; + #endif + SizeT outProcessed; + res = LzmaDecode(&state, + #ifdef _LZMA_IN_CB + &g_InBuffer.InCallback, + #else + inStream, compressedSize, &inProcessed, + #endif + outStream, outSizeFull, &outProcessed); + if (res != 0) + { + sprintf(rs + strlen(rs), "\nDecoding error = %d\n", res); + res = 1; + } + else if (outFile != 0) + { + if (!MyWriteFileAndCheck(outFile, outStream, (size_t)outProcessed)) + { + PrintError(rs, kCantWriteMessage); + res = 1; + } + } + } + #endif + + free(state.Probs); + #ifdef _LZMA_OUT_READ + free(state.Dictionary); + #else + free(outStream); + #endif + #ifndef _LZMA_IN_CB + free(inStream); + #endif + return res; +} + +int main2(int numArgs, const char *args[], char *rs) +{ + FILE *inFile = 0; + FILE *outFile = 0; + int res; + + sprintf(rs + strlen(rs), "\nLZMA Decoder 4.26 Copyright (c) 1999-2005 Igor Pavlov 2005-08-05\n"); + if (numArgs < 2 || numArgs > 3) + { + sprintf(rs + strlen(rs), "\nUsage: lzmadec file.lzma [outFile]\n"); + return 1; + } + + inFile = fopen(args[1], "rb"); + if (inFile == 0) + return PrintError(rs, "Can not open input file"); + + if (numArgs > 2) + { + outFile = fopen(args[2], "wb+"); + if (outFile == 0) + return PrintError(rs, "Can not open output file"); + } + + res = main3(inFile, outFile, rs); + + if (outFile != 0) + fclose(outFile); + fclose(inFile); + return res; +} + +int main(int numArgs, const char *args[]) +{ + char rs[800] = { 0 }; + int res = main2(numArgs, args, rs); + printf(rs); + return res; +} diff --git a/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTypes.h b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTypes.h new file mode 100644 index 0000000..9c27290 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Compress/Lzma/LzmaTypes.h @@ -0,0 +1,45 @@ +/* +LzmaTypes.h + +Types for LZMA Decoder + +This file written and distributed to public domain by Igor Pavlov. +This file is part of LZMA SDK 4.40 (2006-05-01) +*/ + +#ifndef __LZMATYPES_H +#define __LZMATYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +#ifdef _LZMA_UINT32_IS_ULONG +typedef unsigned long UInt32; +#else +typedef unsigned int UInt32; +#endif +#endif + +/* #define _LZMA_NO_SYSTEM_SIZE_T */ +/* You can use it, if you don't want */ + +#ifndef _7ZIP_SIZET_DEFINED +#define _7ZIP_SIZET_DEFINED +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +#include +typedef size_t SizeT; +#endif +#endif + +#endif diff --git a/3rdparty/physfs/lzma/C/CpuArch.h b/3rdparty/physfs/lzma/C/CpuArch.h new file mode 100644 index 0000000..26ce8b1 --- /dev/null +++ b/3rdparty/physfs/lzma/C/CpuArch.h @@ -0,0 +1,18 @@ +/* CpuArch.h */ + +#ifndef __CPUARCH_H +#define __CPUARCH_H + +/* +LITTLE_ENDIAN_UNALIGN means: + 1) CPU is LITTLE_ENDIAN + 2) it's allowed to make unaligned memory accesses +if LITTLE_ENDIAN_UNALIGN is not defined, it means that we don't know +about these properties of platform. +*/ + +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__) +#define LITTLE_ENDIAN_UNALIGN +#endif + +#endif diff --git a/3rdparty/physfs/lzma/C/IStream.h b/3rdparty/physfs/lzma/C/IStream.h new file mode 100644 index 0000000..5821848 --- /dev/null +++ b/3rdparty/physfs/lzma/C/IStream.h @@ -0,0 +1,19 @@ +/* IStream.h */ + +#ifndef __C_ISTREAM_H +#define __C_ISTREAM_H + +#include "Types.h" + +typedef struct _ISeqInStream +{ + HRes (*Read)(void *object, void *data, UInt32 size, UInt32 *processedSize); +} ISeqInStream; + +typedef struct _ISzAlloc +{ + void *(*Alloc)(size_t size); + void (*Free)(void *address); /* address can be 0 */ +} ISzAlloc; + +#endif diff --git a/3rdparty/physfs/lzma/C/Sort.c b/3rdparty/physfs/lzma/C/Sort.c new file mode 100644 index 0000000..b30cd6a --- /dev/null +++ b/3rdparty/physfs/lzma/C/Sort.c @@ -0,0 +1,92 @@ +/* Sort.c */ + +#include "Sort.h" + +#define HeapSortDown(p, k, size, temp) \ + { for (;;) { \ + UInt32 s = (k << 1); \ + if (s > size) break; \ + if (s < size && p[s + 1] > p[s]) s++; \ + if (temp >= p[s]) break; \ + p[k] = p[s]; k = s; \ + } p[k] = temp; } + +void HeapSort(UInt32 *p, UInt32 size) +{ + if (size <= 1) + return; + p--; + { + UInt32 i = size / 2; + do + { + UInt32 temp = p[i]; + UInt32 k = i; + HeapSortDown(p, k, size, temp) + } + while(--i != 0); + } + /* + do + { + UInt32 k = 1; + UInt32 temp = p[size]; + p[size--] = p[1]; + HeapSortDown(p, k, size, temp) + } + while (size > 1); + */ + while (size > 3) + { + UInt32 temp = p[size]; + UInt32 k = (p[3] > p[2]) ? 3 : 2; + p[size--] = p[1]; + p[1] = p[k]; + HeapSortDown(p, k, size, temp) + } + { + UInt32 temp = p[size]; + p[size] = p[1]; + if (size > 2 && p[2] < temp) + { + p[1] = p[2]; + p[2] = temp; + } + else + p[1] = temp; + } +} + +/* +#define HeapSortRefDown(p, vals, n, size, temp) \ + { UInt32 k = n; UInt32 val = vals[temp]; for (;;) { \ + UInt32 s = (k << 1); \ + if (s > size) break; \ + if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \ + if (val >= vals[p[s]]) break; \ + p[k] = p[s]; k = s; \ + } p[k] = temp; } + +void HeapSortRef(UInt32 *p, UInt32 *vals, UInt32 size) +{ + if (size <= 1) + return; + p--; + { + UInt32 i = size / 2; + do + { + UInt32 temp = p[i]; + HeapSortRefDown(p, vals, i, size, temp); + } + while(--i != 0); + } + do + { + UInt32 temp = p[size]; + p[size--] = p[1]; + HeapSortRefDown(p, vals, 1, size, temp); + } + while (size > 1); +} +*/ \ No newline at end of file diff --git a/3rdparty/physfs/lzma/C/Sort.h b/3rdparty/physfs/lzma/C/Sort.h new file mode 100644 index 0000000..896243c --- /dev/null +++ b/3rdparty/physfs/lzma/C/Sort.h @@ -0,0 +1,11 @@ +/* Sort.h */ + +#ifndef __7Z_Sort_H +#define __7Z_Sort_H + +#include "Types.h" + +void HeapSort(UInt32 *p, UInt32 size); +/* void HeapSortRef(UInt32 *p, UInt32 *vals, UInt32 size); */ + +#endif diff --git a/3rdparty/physfs/lzma/C/Threads.c b/3rdparty/physfs/lzma/C/Threads.c new file mode 100644 index 0000000..def4817 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Threads.c @@ -0,0 +1,106 @@ +/* Threads.c */ + +#include "Threads.h" +#include + +HRes GetError() +{ + DWORD res = GetLastError(); + return (res) ? (HRes)(res) : SZE_FAIL; +} + +HRes BoolToHRes(int v) { return v ? SZ_OK : GetError(); } +HRes BOOLToHRes(BOOL v) { return v ? SZ_OK : GetError(); } + +HRes MyCloseHandle(HANDLE *h) +{ + if (*h != NULL) + if (!CloseHandle(*h)) + return GetError(); + *h = NULL; + return SZ_OK; +} + +HRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) +{ + unsigned threadId; /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ + thread->handle = + /* CreateThread(0, 0, startAddress, parameter, 0, &threadId); */ + (HANDLE)_beginthreadex(NULL, 0, startAddress, parameter, 0, &threadId); + /* maybe we must use errno here, but probably GetLastError() is also OK. */ + return BoolToHRes(thread->handle != 0); +} + +HRes WaitObject(HANDLE h) +{ + return (HRes)WaitForSingleObject(h, INFINITE); +} + +HRes Thread_Wait(CThread *thread) +{ + if (thread->handle == NULL) + return 1; + return WaitObject(thread->handle); +} + +HRes Thread_Close(CThread *thread) +{ + return MyCloseHandle(&thread->handle); +} + +HRes Event_Create(CEvent *p, BOOL manualReset, int initialSignaled) +{ + p->handle = CreateEvent(NULL, manualReset, (initialSignaled ? TRUE : FALSE), NULL); + return BoolToHRes(p->handle != 0); +} + +HRes ManualResetEvent_Create(CManualResetEvent *p, int initialSignaled) + { return Event_Create(p, TRUE, initialSignaled); } +HRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) + { return ManualResetEvent_Create(p, 0); } + +HRes AutoResetEvent_Create(CAutoResetEvent *p, int initialSignaled) + { return Event_Create(p, FALSE, initialSignaled); } +HRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) + { return AutoResetEvent_Create(p, 0); } + +HRes Event_Set(CEvent *p) { return BOOLToHRes(SetEvent(p->handle)); } +HRes Event_Reset(CEvent *p) { return BOOLToHRes(ResetEvent(p->handle)); } +HRes Event_Wait(CEvent *p) { return WaitObject(p->handle); } +HRes Event_Close(CEvent *p) { return MyCloseHandle(&p->handle); } + + +HRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount) +{ + p->handle = CreateSemaphore(NULL, (LONG)initiallyCount, (LONG)maxCount, NULL); + return BoolToHRes(p->handle != 0); +} + +HRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount) +{ + return BOOLToHRes(ReleaseSemaphore(p->handle, releaseCount, previousCount)); +} +HRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount) +{ + return Semaphore_Release(p, (LONG)releaseCount, NULL); +} +HRes Semaphore_Release1(CSemaphore *p) +{ + return Semaphore_ReleaseN(p, 1); +} + +HRes Semaphore_Wait(CSemaphore *p) { return WaitObject(p->handle); } +HRes Semaphore_Close(CSemaphore *p) { return MyCloseHandle(&p->handle); } + +HRes CriticalSection_Init(CCriticalSection *p) +{ + /* InitializeCriticalSection can raise only STATUS_NO_MEMORY exception */ + __try + { + InitializeCriticalSection(p); + /* InitializeCriticalSectionAndSpinCount(p, 0); */ + } + __except (EXCEPTION_EXECUTE_HANDLER) { return SZE_OUTOFMEMORY; } + return SZ_OK; +} + diff --git a/3rdparty/physfs/lzma/C/Threads.h b/3rdparty/physfs/lzma/C/Threads.h new file mode 100644 index 0000000..c024dcc --- /dev/null +++ b/3rdparty/physfs/lzma/C/Threads.h @@ -0,0 +1,69 @@ +/* Threads.h */ + +#ifndef __7Z_THRESDS_H +#define __7Z_THRESDS_H + +#include + +#include "Types.h" + +typedef struct _CThread +{ + HANDLE handle; +} CThread; + +#define Thread_Construct(thread) (thread)->handle = NULL +#define Thread_WasCreated(thread) ((thread)->handle != NULL) + +typedef unsigned THREAD_FUNC_RET_TYPE; +#define THREAD_FUNC_CALL_TYPE StdCall +#define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE + +HRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter); +HRes Thread_Wait(CThread *thread); +HRes Thread_Close(CThread *thread); + +typedef struct _CEvent +{ + HANDLE handle; +} CEvent; + +typedef CEvent CAutoResetEvent; +typedef CEvent CManualResetEvent; + +#define Event_Construct(event) (event)->handle = NULL +#define Event_IsCreated(event) ((event)->handle != NULL) + +HRes ManualResetEvent_Create(CManualResetEvent *event, int initialSignaled); +HRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *event); +HRes AutoResetEvent_Create(CAutoResetEvent *event, int initialSignaled); +HRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *event); +HRes Event_Set(CEvent *event); +HRes Event_Reset(CEvent *event); +HRes Event_Wait(CEvent *event); +HRes Event_Close(CEvent *event); + + +typedef struct _CSemaphore +{ + HANDLE handle; +} CSemaphore; + +#define Semaphore_Construct(p) (p)->handle = NULL + +HRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount); +HRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num); +HRes Semaphore_Release1(CSemaphore *p); +HRes Semaphore_Wait(CSemaphore *p); +HRes Semaphore_Close(CSemaphore *p); + + +typedef CRITICAL_SECTION CCriticalSection; + +HRes CriticalSection_Init(CCriticalSection *p); +#define CriticalSection_Delete(p) DeleteCriticalSection(p) +#define CriticalSection_Enter(p) EnterCriticalSection(p) +#define CriticalSection_Leave(p) LeaveCriticalSection(p) + +#endif + diff --git a/3rdparty/physfs/lzma/C/Types.h b/3rdparty/physfs/lzma/C/Types.h new file mode 100644 index 0000000..368cc31 --- /dev/null +++ b/3rdparty/physfs/lzma/C/Types.h @@ -0,0 +1,100 @@ +/* 7zTypes.h */ + +#ifndef __C_TYPES_H +#define __C_TYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +#ifdef _LZMA_UINT32_IS_ULONG +typedef unsigned long UInt32; +#else +typedef unsigned int UInt32; +#endif +#endif + +#ifndef _7ZIP_INT32_DEFINED +#define _7ZIP_INT32_DEFINED +#ifdef _LZMA_INT32_IS_ULONG +typedef long Int32; +#else +typedef int Int32; +#endif +#endif + +/* #define _SZ_NO_INT_64 */ +/* define it your compiler doesn't support long long int */ + +#ifndef _7ZIP_UINT64_DEFINED +#define _7ZIP_UINT64_DEFINED +#ifdef _SZ_NO_INT_64 +typedef unsigned long UInt64; +#else +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 UInt64; +#else +typedef unsigned long long int UInt64; +#endif +#endif +#endif + + +/* #define _SZ_FILE_SIZE_32 */ +/* You can define _SZ_FILE_SIZE_32, if you don't need support for files larger than 4 GB*/ + +#ifndef CFileSize +#ifdef _SZ_FILE_SIZE_32 +typedef UInt32 CFileSize; +#else +typedef UInt64 CFileSize; +#endif +#endif + +#define SZ_RESULT int + +typedef int HRes; +#define RES_OK (0) + +#define SZ_OK (0) +#define SZE_DATA_ERROR (1) +#define SZE_CRC_ERROR (3) +#define SZE_ARCHIVE_ERROR (6) + +#define SZE_OUTOFMEMORY (0x8007000EL) +#define SZE_NOTIMPL (0x80004001L) +#define SZE_FAIL (0x80004005L) +#define SZE_INVALIDARG (0x80070057L) + + +#ifndef RINOK +#define RINOK(x) { HRes __result_ = (x); if(__result_ != 0) return __result_; } +#endif + +typedef int Bool; +#define True 1 +#define False 0 + +#ifdef _MSC_VER +#define StdCall __stdcall +#else +#define StdCall +#endif + +#if _MSC_VER >= 1300 +#define MY_FAST_CALL __declspec(noinline) __fastcall +#elif defined( _MSC_VER) +#define MY_FAST_CALL __fastcall +#else +#define MY_FAST_CALL +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7z.ico b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7z.ico new file mode 100755 index 0000000..319753a Binary files /dev/null and b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7z.ico differ diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp new file mode 100644 index 0000000..6774fc4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp @@ -0,0 +1,3 @@ +// CompressionMethod.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h new file mode 100644 index 0000000..5753606 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h @@ -0,0 +1,50 @@ +// 7zCompressionMode.h + +#ifndef __7Z_COMPRESSION_MODE_H +#define __7Z_COMPRESSION_MODE_H + +#include "../../../Common/MyString.h" + +#include "../../../Windows/PropVariant.h" + +#include "../../Common/MethodProps.h" + +namespace NArchive { +namespace N7z { + +struct CMethodFull: public CMethod +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } +}; + +struct CBind +{ + UInt32 InCoder; + UInt32 InStream; + UInt32 OutCoder; + UInt32 OutStream; +}; + +struct CCompressionMethodMode +{ + CObjectVector Methods; + CRecordVector Binds; + #ifdef COMPRESS_MT + UInt32 NumThreads; + #endif + bool PasswordIsDefined; + UString Password; + + bool IsEmpty() const { return (Methods.IsEmpty() && !PasswordIsDefined); } + CCompressionMethodMode(): PasswordIsDefined(false) + #ifdef COMPRESS_MT + , NumThreads(1) + #endif + {} +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.cpp new file mode 100644 index 0000000..0f81de4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.cpp @@ -0,0 +1,330 @@ +// 7zDecode.cpp + +#include "StdAfx.h" + +#include "7zDecode.h" + +#include "../../IPassword.h" +#include "../../Common/LockedStream.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" + +namespace NArchive { +namespace N7z { + +static void ConvertFolderItemInfoToBindInfo(const CFolder &folder, + CBindInfoEx &bindInfo) +{ + bindInfo.Clear(); + int i; + for (i = 0; i < folder.BindPairs.Size(); i++) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex; + bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex; + bindInfo.BindPairs.Add(bindPair); + } + UInt32 outStreamIndex = 0; + for (i = 0; i < folder.Coders.Size(); i++) + { + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + const CCoderInfo &coderInfo = folder.Coders[i]; + coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams; + coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams; + bindInfo.Coders.Add(coderStreamsInfo); + bindInfo.CoderMethodIDs.Add(coderInfo.MethodID); + for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++) + if (folder.FindBindPairForOutStream(outStreamIndex) < 0) + bindInfo.OutStreams.Add(outStreamIndex); + } + for (i = 0; i < folder.PackStreams.Size(); i++) + bindInfo.InStreams.Add((UInt32)folder.PackStreams[i]); +} + +static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1, + const NCoderMixer::CCoderStreamsInfo &a2) +{ + return (a1.NumInStreams == a2.NumInStreams) && + (a1.NumOutStreams == a2.NumOutStreams); +} + +static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2) +{ + return (a1.InIndex == a2.InIndex) && + (a1.OutIndex == a2.OutIndex); +} + +static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2) +{ + if (a1.Coders.Size() != a2.Coders.Size()) + return false; + int i; + for (i = 0; i < a1.Coders.Size(); i++) + if (!AreCodersEqual(a1.Coders[i], a2.Coders[i])) + return false; + if (a1.BindPairs.Size() != a2.BindPairs.Size()) + return false; + for (i = 0; i < a1.BindPairs.Size(); i++) + if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i])) + return false; + for (i = 0; i < a1.CoderMethodIDs.Size(); i++) + if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i]) + return false; + if (a1.InStreams.Size() != a2.InStreams.Size()) + return false; + if (a1.OutStreams.Size() != a2.OutStreams.Size()) + return false; + return true; +} + +CDecoder::CDecoder(bool multiThread) +{ + #ifndef _ST_MODE + multiThread = true; + #endif + _multiThread = multiThread; + _bindInfoExPrevIsDefined = false; +} + +HRESULT CDecoder::Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + UInt64 startPos, + const UInt64 *packSizes, + const CFolder &folderInfo, + ISequentialOutStream *outStream, + ICompressProgressInfo *compressProgress + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + #ifdef COMPRESS_MT + , bool mtMode, UInt32 numThreads + #endif + ) +{ + CObjectVector< CMyComPtr > inStreams; + + CLockedInStream lockedInStream; + lockedInStream.Init(inStream); + + for (int j = 0; j < folderInfo.PackStreams.Size(); j++) + { + CLockedSequentialInStreamImp *lockedStreamImpSpec = new + CLockedSequentialInStreamImp; + CMyComPtr lockedStreamImp = lockedStreamImpSpec; + lockedStreamImpSpec->Init(&lockedInStream, startPos); + startPos += packSizes[j]; + + CLimitedSequentialInStream *streamSpec = new + CLimitedSequentialInStream; + CMyComPtr inStream = streamSpec; + streamSpec->SetStream(lockedStreamImp); + streamSpec->Init(packSizes[j]); + inStreams.Add(inStream); + } + + int numCoders = folderInfo.Coders.Size(); + + CBindInfoEx bindInfo; + ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo); + bool createNewCoders; + if (!_bindInfoExPrevIsDefined) + createNewCoders = true; + else + createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev); + if (createNewCoders) + { + int i; + _decoders.Clear(); + // _decoders2.Clear(); + + _mixerCoder.Release(); + + if (_multiThread) + { + _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT; + _mixerCoder = _mixerCoderMTSpec; + _mixerCoderCommon = _mixerCoderMTSpec; + } + else + { + #ifdef _ST_MODE + _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST; + _mixerCoder = _mixerCoderSTSpec; + _mixerCoderCommon = _mixerCoderSTSpec; + #endif + } + RINOK(_mixerCoderCommon->SetBindInfo(bindInfo)); + + for (i = 0; i < numCoders; i++) + { + const CCoderInfo &coderInfo = folderInfo.Coders[i]; + + + CMyComPtr decoder; + CMyComPtr decoder2; + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + coderInfo.MethodID, decoder, decoder2, false)); + CMyComPtr decoderUnknown; + if (coderInfo.IsSimpleCoder()) + { + if (decoder == 0) + return E_NOTIMPL; + + decoderUnknown = (IUnknown *)decoder; + + if (_multiThread) + _mixerCoderMTSpec->AddCoder(decoder); + #ifdef _ST_MODE + else + _mixerCoderSTSpec->AddCoder(decoder, false); + #endif + } + else + { + if (decoder2 == 0) + return E_NOTIMPL; + decoderUnknown = (IUnknown *)decoder2; + if (_multiThread) + _mixerCoderMTSpec->AddCoder2(decoder2); + #ifdef _ST_MODE + else + _mixerCoderSTSpec->AddCoder2(decoder2, false); + #endif + } + _decoders.Add(decoderUnknown); + #ifdef EXTERNAL_CODECS + CMyComPtr setCompressCodecsInfo; + decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo)); + } + #endif + } + _bindInfoExPrev = bindInfo; + _bindInfoExPrevIsDefined = true; + } + int i; + _mixerCoderCommon->ReInit(); + + UInt32 packStreamIndex = 0, unPackStreamIndex = 0; + UInt32 coderIndex = 0; + // UInt32 coder2Index = 0; + + for (i = 0; i < numCoders; i++) + { + const CCoderInfo &coderInfo = folderInfo.Coders[i]; + CMyComPtr &decoder = _decoders[coderIndex]; + + { + CMyComPtr setDecoderProperties; + decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); + if (setDecoderProperties) + { + const CByteBuffer &properties = coderInfo.Properties; + size_t size = properties.GetCapacity(); + if (size > 0xFFFFFFFF) + return E_NOTIMPL; + if (size > 0) + { + RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)properties, (UInt32)size)); + } + } + } + + #ifdef COMPRESS_MT + if (mtMode) + { + CMyComPtr setCoderMt; + decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(numThreads)); + } + } + #endif + + #ifndef _NO_CRYPTO + { + CMyComPtr cryptoSetPassword; + decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); + if (cryptoSetPassword) + { + if (getTextPassword == 0) + return E_FAIL; + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)); + CByteBuffer buffer; + UString unicodePassword(password); + const UInt32 sizeInBytes = unicodePassword.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < unicodePassword.Length(); i++) + { + wchar_t c = unicodePassword[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + RINOK(cryptoSetPassword->CryptoSetPassword( + (const Byte *)buffer, sizeInBytes)); + } + } + #endif + + coderIndex++; + + UInt32 numInStreams = (UInt32)coderInfo.NumInStreams; + UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams; + CRecordVector packSizesPointers; + CRecordVector unPackSizesPointers; + packSizesPointers.Reserve(numInStreams); + unPackSizesPointers.Reserve(numOutStreams); + UInt32 j; + for (j = 0; j < numOutStreams; j++, unPackStreamIndex++) + unPackSizesPointers.Add(&folderInfo.UnPackSizes[unPackStreamIndex]); + + for (j = 0; j < numInStreams; j++, packStreamIndex++) + { + int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex); + if (bindPairIndex >= 0) + packSizesPointers.Add( + &folderInfo.UnPackSizes[(UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]); + else + { + int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex); + if (index < 0) + return E_FAIL; + packSizesPointers.Add(&packSizes[index]); + } + } + + _mixerCoderCommon->SetCoderInfo(i, + &packSizesPointers.Front(), + &unPackSizesPointers.Front()); + } + UInt32 mainCoder, temp; + bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp); + + if (_multiThread) + _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder); + /* + else + _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);; + */ + + if (numCoders == 0) + return 0; + CRecordVector inStreamPointers; + inStreamPointers.Reserve(inStreams.Size()); + for (i = 0; i < inStreams.Size(); i++) + inStreamPointers.Add(inStreams[i]); + ISequentialOutStream *outStreamPointer = outStream; + return _mixerCoder->Code(&inStreamPointers.Front(), NULL, + inStreams.Size(), &outStreamPointer, NULL, 1, compressProgress); +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.h new file mode 100644 index 0000000..7c10dfe --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zDecode.h @@ -0,0 +1,68 @@ +// 7zDecode.h + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "../../IStream.h" +#include "../../IPassword.h" + +#include "../Common/CoderMixer2.h" +#include "../Common/CoderMixer2MT.h" +#ifdef _ST_MODE +#include "../Common/CoderMixer2ST.h" +#endif + +#include "../../Common/CreateCoder.h" + +#include "7zItem.h" + +namespace NArchive { +namespace N7z { + +struct CBindInfoEx: public NCoderMixer::CBindInfo +{ + CRecordVector CoderMethodIDs; + void Clear() + { + CBindInfo::Clear(); + CoderMethodIDs.Clear(); + } +}; + +class CDecoder +{ + bool _bindInfoExPrevIsDefined; + CBindInfoEx _bindInfoExPrev; + + bool _multiThread; + #ifdef _ST_MODE + NCoderMixer::CCoderMixer2ST *_mixerCoderSTSpec; + #endif + NCoderMixer::CCoderMixer2MT *_mixerCoderMTSpec; + NCoderMixer::CCoderMixer2 *_mixerCoderCommon; + + CMyComPtr _mixerCoder; + CObjectVector > _decoders; + // CObjectVector > _decoders2; +public: + CDecoder(bool multiThread); + HRESULT Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + UInt64 startPos, + const UInt64 *packSizes, + const CFolder &folder, + ISequentialOutStream *outStream, + ICompressProgressInfo *compressProgress + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPasswordSpec + #endif + #ifdef COMPRESS_MT + , bool mtMode, UInt32 numThreads + #endif + ); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.cpp new file mode 100644 index 0000000..3a5cfcd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.cpp @@ -0,0 +1,453 @@ +// Encode.cpp + +#include "StdAfx.h" + +#include "7zEncode.h" +#include "7zSpecStream.h" + +#include "../../IPassword.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/InOutTempBuffer.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" + +static const UInt64 k_AES = 0x06F10701; +static const UInt64 k_BCJ = 0x03030103; +static const UInt64 k_BCJ2 = 0x0303011B; + +namespace NArchive { +namespace N7z { + +static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo, + const CRecordVector decompressionMethods, + CFolder &folder) +{ + folder.Coders.Clear(); + // bindInfo.CoderMethodIDs.Clear(); + // folder.OutStreams.Clear(); + folder.PackStreams.Clear(); + folder.BindPairs.Clear(); + int i; + for (i = 0; i < bindInfo.BindPairs.Size(); i++) + { + CBindPair bindPair; + bindPair.InIndex = bindInfo.BindPairs[i].InIndex; + bindPair.OutIndex = bindInfo.BindPairs[i].OutIndex; + folder.BindPairs.Add(bindPair); + } + for (i = 0; i < bindInfo.Coders.Size(); i++) + { + CCoderInfo coderInfo; + const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; + coderInfo.NumInStreams = coderStreamsInfo.NumInStreams; + coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams; + coderInfo.MethodID = decompressionMethods[i]; + folder.Coders.Add(coderInfo); + } + for (i = 0; i < bindInfo.InStreams.Size(); i++) + folder.PackStreams.Add(bindInfo.InStreams[i]); +} + +HRESULT CEncoder::CreateMixerCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + const UInt64 *inSizeForReduce) +{ + _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT; + _mixerCoder = _mixerCoderSpec; + RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo)); + for (int i = 0; i < _options.Methods.Size(); i++) + { + const CMethodFull &methodFull = _options.Methods[i]; + _codersInfo.Add(CCoderInfo()); + CCoderInfo &encodingInfo = _codersInfo.Back(); + encodingInfo.MethodID = methodFull.Id; + CMyComPtr encoder; + CMyComPtr encoder2; + + + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodFull.Id, encoder, encoder2, true)); + + if (!encoder && !encoder2) + return E_FAIL; + + CMyComPtr encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2; + + #ifdef COMPRESS_MT + { + CMyComPtr setCoderMt; + encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads)); + } + } + #endif + + + RINOK(SetMethodProperties(methodFull, inSizeForReduce, encoderCommon)); + + /* + CMyComPtr resetSalt; + encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt); + if (resetSalt != NULL) + { + resetSalt->ResetSalt(); + } + */ + + #ifdef EXTERNAL_CODECS + CMyComPtr setCompressCodecsInfo; + encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo)); + } + #endif + + CMyComPtr cryptoSetPassword; + encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); + + if (cryptoSetPassword) + { + CByteBuffer buffer; + const UInt32 sizeInBytes = _options.Password.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < _options.Password.Length(); i++) + { + wchar_t c = _options.Password[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); + } + + if (encoder) + _mixerCoderSpec->AddCoder(encoder); + else + _mixerCoderSpec->AddCoder2(encoder2); + } + return S_OK; +} + +HRESULT CEncoder::Encode( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, + const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, + CFolder &folderItem, + ISequentialOutStream *outStream, + CRecordVector &packSizes, + ICompressProgressInfo *compressProgress) +{ + RINOK(EncoderConstr()); + + if (_mixerCoderSpec == NULL) + { + RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce)); + } + _mixerCoderSpec->ReInit(); + // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress); + + CObjectVector inOutTempBuffers; + CObjectVector tempBufferSpecs; + CObjectVector > tempBuffers; + int numMethods = _bindInfo.Coders.Size(); + int i; + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + inOutTempBuffers.Add(CInOutTempBuffer()); + inOutTempBuffers.Back().Create(); + inOutTempBuffers.Back().InitWriting(); + } + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + CSequentialOutTempBufferImp *tempBufferSpec = + new CSequentialOutTempBufferImp; + CMyComPtr tempBuffer = tempBufferSpec; + tempBufferSpec->Init(&inOutTempBuffers[i - 1]); + tempBuffers.Add(tempBuffer); + tempBufferSpecs.Add(tempBufferSpec); + } + + for (i = 0; i < numMethods; i++) + _mixerCoderSpec->SetCoderInfo(i, NULL, NULL); + + if (_bindInfo.InStreams.IsEmpty()) + return E_FAIL; + UInt32 mainCoderIndex, mainStreamIndex; + _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex); + + if (inStreamSize != NULL) + { + CRecordVector sizePointers; + for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++) + if (i == mainStreamIndex) + sizePointers.Add(inStreamSize); + else + sizePointers.Add(NULL); + _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL); + } + + + // UInt64 outStreamStartPos; + // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos)); + + CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = + new CSequentialInStreamSizeCount2; + CMyComPtr inStreamSizeCount = inStreamSizeCountSpec; + CSequentialOutStreamSizeCount *outStreamSizeCountSpec = + new CSequentialOutStreamSizeCount; + CMyComPtr outStreamSizeCount = outStreamSizeCountSpec; + + inStreamSizeCountSpec->Init(inStream); + outStreamSizeCountSpec->SetStream(outStream); + outStreamSizeCountSpec->Init(); + + CRecordVector inStreamPointers; + CRecordVector outStreamPointers; + inStreamPointers.Add(inStreamSizeCount); + outStreamPointers.Add(outStreamSizeCount); + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + outStreamPointers.Add(tempBuffers[i - 1]); + + for (i = 0; i < _codersInfo.Size(); i++) + { + CCoderInfo &encodingInfo = _codersInfo[i]; + + CMyComPtr resetInitVector; + _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector); + if (resetInitVector != NULL) + { + resetInitVector->ResetInitVector(); + } + + CMyComPtr writeCoderProperties; + _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); + if (writeCoderProperties != NULL) + { + CSequentialOutStreamImp *outStreamSpec = new CSequentialOutStreamImp; + CMyComPtr outStream(outStreamSpec); + outStreamSpec->Init(); + writeCoderProperties->WriteCoderProperties(outStream); + size_t size = outStreamSpec->GetSize(); + encodingInfo.Properties.SetCapacity(size); + memmove(encodingInfo.Properties, outStreamSpec->GetBuffer(), size); + } + } + + UInt32 progressIndex = mainCoderIndex; + + for (i = 0; i < _codersInfo.Size(); i++) + { + const CCoderInfo &e = _codersInfo[i]; + if ((e.MethodID == k_BCJ || e.MethodID == k_BCJ2) && i + 1 < _codersInfo.Size()) + progressIndex = i + 1; + } + + _mixerCoderSpec->SetProgressCoderIndex(progressIndex); + + RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1, + &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress)); + + ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, + folderItem); + + packSizes.Add(outStreamSizeCountSpec->GetSize()); + + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1]; + inOutTempBuffer.FlushWrite(); + inOutTempBuffer.InitReading(); + inOutTempBuffer.WriteToStream(outStream); + packSizes.Add(inOutTempBuffer.GetDataSize()); + } + + for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++) + { + int binder = _bindInfo.FindBinderForInStream( + _bindReverseConverter->DestOutToSrcInMap[i]); + UInt64 streamSize; + if (binder < 0) + streamSize = inStreamSizeCountSpec->GetSize(); + else + streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder); + folderItem.UnPackSizes.Add(streamSize); + } + for (i = numMethods - 1; i >= 0; i--) + folderItem.Coders[numMethods - 1 - i].Properties = _codersInfo[i].Properties; + return S_OK; +} + + +CEncoder::CEncoder(const CCompressionMethodMode &options): + _bindReverseConverter(0), + _constructed(false) +{ + if (options.IsEmpty()) + throw 1; + + _options = options; + _mixerCoderSpec = NULL; +} + +HRESULT CEncoder::EncoderConstr() +{ + if (_constructed) + return S_OK; + if (_options.Methods.IsEmpty()) + { + // it has only password method; + if (!_options.PasswordIsDefined) + throw 1; + if (!_options.Binds.IsEmpty()) + throw 1; + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + CMethodFull method; + + method.NumInStreams = 1; + method.NumOutStreams = 1; + coderStreamsInfo.NumInStreams = 1; + coderStreamsInfo.NumOutStreams = 1; + method.Id = k_AES; + + _options.Methods.Add(method); + _bindInfo.Coders.Add(coderStreamsInfo); + + _bindInfo.InStreams.Add(0); + _bindInfo.OutStreams.Add(0); + } + else + { + + UInt32 numInStreams = 0, numOutStreams = 0; + int i; + for (i = 0; i < _options.Methods.Size(); i++) + { + const CMethodFull &methodFull = _options.Methods[i]; + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + coderStreamsInfo.NumInStreams = methodFull.NumOutStreams; + coderStreamsInfo.NumOutStreams = methodFull.NumInStreams; + if (_options.Binds.IsEmpty()) + { + if (i < _options.Methods.Size() - 1) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams; + bindPair.OutIndex = numOutStreams; + _bindInfo.BindPairs.Add(bindPair); + } + else + _bindInfo.OutStreams.Insert(0, numOutStreams); + for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++) + _bindInfo.OutStreams.Add(numOutStreams + j); + } + + numInStreams += coderStreamsInfo.NumInStreams; + numOutStreams += coderStreamsInfo.NumOutStreams; + + _bindInfo.Coders.Add(coderStreamsInfo); + } + + if (!_options.Binds.IsEmpty()) + { + for (i = 0; i < _options.Binds.Size(); i++) + { + NCoderMixer::CBindPair bindPair; + const CBind &bind = _options.Binds[i]; + bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream; + bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream; + _bindInfo.BindPairs.Add(bindPair); + } + for (i = 0; i < (int)numOutStreams; i++) + if (_bindInfo.FindBinderForOutStream(i) == -1) + _bindInfo.OutStreams.Add(i); + } + + for (i = 0; i < (int)numInStreams; i++) + if (_bindInfo.FindBinderForInStream(i) == -1) + _bindInfo.InStreams.Add(i); + + if (_bindInfo.InStreams.IsEmpty()) + throw 1; // this is error + + // Make main stream first in list + int inIndex = _bindInfo.InStreams[0]; + for (;;) + { + UInt32 coderIndex, coderStreamIndex; + _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex); + UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex); + int binder = _bindInfo.FindBinderForOutStream(outIndex); + if (binder >= 0) + { + inIndex = _bindInfo.BindPairs[binder].InIndex; + continue; + } + for (i = 0; i < _bindInfo.OutStreams.Size(); i++) + if (_bindInfo.OutStreams[i] == outIndex) + { + _bindInfo.OutStreams.Delete(i); + _bindInfo.OutStreams.Insert(0, outIndex); + break; + } + break; + } + + if (_options.PasswordIsDefined) + { + int numCryptoStreams = _bindInfo.OutStreams.Size(); + + for (i = 0; i < numCryptoStreams; i++) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = numInStreams + i; + bindPair.OutIndex = _bindInfo.OutStreams[i]; + _bindInfo.BindPairs.Add(bindPair); + } + _bindInfo.OutStreams.Clear(); + + /* + if (numCryptoStreams == 0) + numCryptoStreams = 1; + */ + + for (i = 0; i < numCryptoStreams; i++) + { + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + CMethodFull method; + method.NumInStreams = 1; + method.NumOutStreams = 1; + coderStreamsInfo.NumInStreams = method.NumOutStreams; + coderStreamsInfo.NumOutStreams = method.NumInStreams; + method.Id = k_AES; + + _options.Methods.Add(method); + _bindInfo.Coders.Add(coderStreamsInfo); + _bindInfo.OutStreams.Add(numOutStreams + i); + } + } + + } + + for (int i = _options.Methods.Size() - 1; i >= 0; i--) + { + const CMethodFull &methodFull = _options.Methods[i]; + _decompressionMethods.Add(methodFull.Id); + } + + _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo); + _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo); + _constructed = true; + return S_OK; +} + +CEncoder::~CEncoder() +{ + delete _bindReverseConverter; +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.h new file mode 100644 index 0000000..4909a6e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zEncode.h @@ -0,0 +1,55 @@ +// 7zEncode.h + +#ifndef __7Z_ENCODE_H +#define __7Z_ENCODE_H + +// #include "../../Common/StreamObjects.h" + +#include "7zCompressionMode.h" + +#include "../Common/CoderMixer2.h" +#include "../Common/CoderMixer2MT.h" +#ifdef _ST_MODE +#include "../Common/CoderMixer2ST.h" +#endif +#include "7zItem.h" + +#include "../../Common/CreateCoder.h" + +namespace NArchive { +namespace N7z { + +class CEncoder +{ + NCoderMixer::CCoderMixer2MT *_mixerCoderSpec; + CMyComPtr _mixerCoder; + + CObjectVector _codersInfo; + + CCompressionMethodMode _options; + NCoderMixer::CBindInfo _bindInfo; + NCoderMixer::CBindInfo _decompressBindInfo; + NCoderMixer::CBindReverseConverter *_bindReverseConverter; + CRecordVector _decompressionMethods; + + HRESULT CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS + const UInt64 *inSizeForReduce); + + bool _constructed; +public: + CEncoder(const CCompressionMethodMode &options); + ~CEncoder(); + HRESULT EncoderConstr(); + HRESULT Encode( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, + const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, + CFolder &folderItem, + ISequentialOutStream *outStream, + CRecordVector &packSizes, + ICompressProgressInfo *compressProgress); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zExtract.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zExtract.cpp new file mode 100644 index 0000000..4297709 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zExtract.cpp @@ -0,0 +1,269 @@ +// 7zExtract.cpp + +#include "StdAfx.h" + +#include "7zHandler.h" +#include "7zFolderOutStream.h" +#include "7zDecode.h" +// #include "7z1Decode.h" + +#include "../../../Common/ComTry.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/LimitedStreams.h" + +namespace NArchive { +namespace N7z { + +struct CExtractFolderInfo +{ + #ifdef _7Z_VOL + int VolumeIndex; + #endif + CNum FileIndex; + CNum FolderIndex; + CBoolVector ExtractStatuses; + UInt64 UnPackSize; + CExtractFolderInfo( + #ifdef _7Z_VOL + int volumeIndex, + #endif + CNum fileIndex, CNum folderIndex): + #ifdef _7Z_VOL + VolumeIndex(volumeIndex), + #endif + FileIndex(fileIndex), + FolderIndex(folderIndex), + UnPackSize(0) + { + if (fileIndex != kNumNoIndex) + { + ExtractStatuses.Reserve(1); + ExtractStatuses.Add(true); + } + }; +}; + +STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems, + Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) +{ + COM_TRY_BEGIN + bool testMode = (testModeSpec != 0); + CMyComPtr extractCallback = extractCallbackSpec; + UInt64 importantTotalUnPacked = 0; + + bool allFilesMode = (numItems == UInt32(-1)); + if (allFilesMode) + numItems = + #ifdef _7Z_VOL + _refs.Size(); + #else + _database.Files.Size(); + #endif + + if(numItems == 0) + return S_OK; + + /* + if(_volumes.Size() != 1) + return E_FAIL; + const CVolume &volume = _volumes.Front(); + const CArchiveDatabaseEx &_database = volume.Database; + IInStream *_inStream = volume.Stream; + */ + + CObjectVector extractFolderInfoVector; + for(UInt32 ii = 0; ii < numItems; ii++) + { + // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex]; + UInt32 ref2Index = allFilesMode ? ii : indices[ii]; + // const CRef2 &ref2 = _refs[ref2Index]; + + // for(UInt32 ri = 0; ri < ref2.Refs.Size(); ri++) + { + #ifdef _7Z_VOL + // const CRef &ref = ref2.Refs[ri]; + const CRef &ref = _refs[ref2Index]; + + int volumeIndex = ref.VolumeIndex; + const CVolume &volume = _volumes[volumeIndex]; + const CArchiveDatabaseEx &database = volume.Database; + UInt32 fileIndex = ref.ItemIndex; + #else + const CArchiveDatabaseEx &database = _database; + UInt32 fileIndex = ref2Index; + #endif + + CNum folderIndex = database.FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex == kNumNoIndex) + { + extractFolderInfoVector.Add(CExtractFolderInfo( + #ifdef _7Z_VOL + volumeIndex, + #endif + fileIndex, kNumNoIndex)); + continue; + } + if (extractFolderInfoVector.IsEmpty() || + folderIndex != extractFolderInfoVector.Back().FolderIndex + #ifdef _7Z_VOL + || volumeIndex != extractFolderInfoVector.Back().VolumeIndex + #endif + ) + { + extractFolderInfoVector.Add(CExtractFolderInfo( + #ifdef _7Z_VOL + volumeIndex, + #endif + kNumNoIndex, folderIndex)); + const CFolder &folderInfo = database.Folders[folderIndex]; + UInt64 unPackSize = folderInfo.GetUnPackSize(); + importantTotalUnPacked += unPackSize; + extractFolderInfoVector.Back().UnPackSize = unPackSize; + } + + CExtractFolderInfo &efi = extractFolderInfoVector.Back(); + + // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex]; + CNum startIndex = database.FolderStartFileIndex[folderIndex]; + for (CNum index = efi.ExtractStatuses.Size(); + index <= fileIndex - startIndex; index++) + { + // UInt64 unPackSize = _database.Files[startIndex + index].UnPackSize; + // Count partial_folder_size + // efi.UnPackSize += unPackSize; + // importantTotalUnPacked += unPackSize; + efi.ExtractStatuses.Add(index == fileIndex - startIndex); + } + } + } + + extractCallback->SetTotal(importantTotalUnPacked); + + CDecoder decoder( + #ifdef _ST_MODE + false + #else + true + #endif + ); + // CDecoder1 decoder; + + UInt64 currentTotalPacked = 0; + UInt64 currentTotalUnPacked = 0; + UInt64 totalFolderUnPacked; + UInt64 totalFolderPacked; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr progress = lps; + lps->Init(extractCallback, false); + + for(int i = 0; i < extractFolderInfoVector.Size(); i++, + currentTotalUnPacked += totalFolderUnPacked, + currentTotalPacked += totalFolderPacked) + { + lps->OutSize = currentTotalUnPacked; + lps->InSize = currentTotalPacked; + RINOK(lps->SetCur()); + + const CExtractFolderInfo &efi = extractFolderInfoVector[i]; + totalFolderUnPacked = efi.UnPackSize; + + totalFolderPacked = 0; + + CFolderOutStream *folderOutStream = new CFolderOutStream; + CMyComPtr outStream(folderOutStream); + + #ifdef _7Z_VOL + const CVolume &volume = _volumes[efi.VolumeIndex]; + const CArchiveDatabaseEx &database = volume.Database; + #else + const CArchiveDatabaseEx &database = _database; + #endif + + CNum startIndex; + if (efi.FileIndex != kNumNoIndex) + startIndex = efi.FileIndex; + else + startIndex = database.FolderStartFileIndex[efi.FolderIndex]; + + + HRESULT result = folderOutStream->Init(&database, + #ifdef _7Z_VOL + volume.StartRef2Index, + #else + 0, + #endif + startIndex, + &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0); + + RINOK(result); + + if (efi.FileIndex != kNumNoIndex) + continue; + + CNum folderIndex = efi.FolderIndex; + const CFolder &folderInfo = database.Folders[folderIndex]; + + totalFolderPacked = _database.GetFolderFullPackSize(folderIndex); + + CNum packStreamIndex = database.FolderStartPackStreamIndex[folderIndex]; + UInt64 folderStartPackPos = database.GetFolderStreamPos(folderIndex, 0); + + #ifndef _NO_CRYPTO + CMyComPtr getTextPassword; + if (extractCallback) + extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); + #endif + + try + { + HRESULT result = decoder.Decode( + EXTERNAL_CODECS_VARS + #ifdef _7Z_VOL + volume.Stream, + #else + _inStream, + #endif + folderStartPackPos, + &database.PackSizes[packStreamIndex], + folderInfo, + outStream, + progress + #ifndef _NO_CRYPTO + , getTextPassword + #endif + #ifdef COMPRESS_MT + , true, _numThreads + #endif + ); + + if (result == S_FALSE) + { + RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError)); + continue; + } + if (result == E_NOTIMPL) + { + RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + if (result != S_OK) + return result; + if (folderOutStream->WasWritingFinished() != S_OK) + { + RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError)); + continue; + } + } + catch(...) + { + RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError)); + continue; + } + } + return S_OK; + COM_TRY_END +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp new file mode 100644 index 0000000..f60a717 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp @@ -0,0 +1,130 @@ +// 7zFolderInStream.cpp + +#include "StdAfx.h" + +#include "7zFolderInStream.h" + +namespace NArchive { +namespace N7z { + +CFolderInStream::CFolderInStream() +{ + _inStreamWithHashSpec = new CSequentialInStreamWithCRC; + _inStreamWithHash = _inStreamWithHashSpec; +} + +void CFolderInStream::Init(IArchiveUpdateCallback *updateCallback, + const UInt32 *fileIndices, UInt32 numFiles) +{ + _updateCallback = updateCallback; + _numFiles = numFiles; + _fileIndex = 0; + _fileIndices = fileIndices; + Processed.Clear(); + CRCs.Clear(); + Sizes.Clear(); + _fileIsOpen = false; + _currentSizeIsDefined = false; +} + +HRESULT CFolderInStream::OpenStream() +{ + _filePos = 0; + while (_fileIndex < _numFiles) + { + _currentSizeIsDefined = false; + CMyComPtr stream; + HRESULT result = _updateCallback->GetStream(_fileIndices[_fileIndex], &stream); + if (result != S_OK && result != S_FALSE) + return result; + _fileIndex++; + _inStreamWithHashSpec->SetStream(stream); + _inStreamWithHashSpec->Init(); + if (!stream) + { + RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + Sizes.Add(0); + Processed.Add(result == S_OK); + AddDigest(); + continue; + } + CMyComPtr streamGetSize; + if (stream.QueryInterface(IID_IStreamGetSize, &streamGetSize) == S_OK) + { + if(streamGetSize) + { + _currentSizeIsDefined = true; + RINOK(streamGetSize->GetSize(&_currentSize)); + } + } + + _fileIsOpen = true; + return S_OK; + } + return S_OK; +} + +void CFolderInStream::AddDigest() +{ + CRCs.Add(_inStreamWithHashSpec->GetCRC()); +} + +HRESULT CFolderInStream::CloseStream() +{ + RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + _inStreamWithHashSpec->ReleaseStream(); + _fileIsOpen = false; + Processed.Add(true); + Sizes.Add(_filePos); + AddDigest(); + return S_OK; +} + +STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize = 0; + while ((_fileIndex < _numFiles || _fileIsOpen) && size > 0) + { + if (_fileIsOpen) + { + UInt32 localProcessedSize; + RINOK(_inStreamWithHash->Read( + ((Byte *)data) + realProcessedSize, size, &localProcessedSize)); + if (localProcessedSize == 0) + { + RINOK(CloseStream()); + continue; + } + realProcessedSize += localProcessedSize; + _filePos += localProcessedSize; + size -= localProcessedSize; + break; + } + else + { + RINOK(OpenStream()); + } + } + if (processedSize != 0) + *processedSize = realProcessedSize; + return S_OK; +} + +STDMETHODIMP CFolderInStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) +{ + *value = 0; + int subStreamIndex = (int)subStream; + if (subStreamIndex < 0 || subStream > Sizes.Size()) + return E_FAIL; + if (subStreamIndex < Sizes.Size()) + { + *value= Sizes[subStreamIndex]; + return S_OK; + } + if (!_currentSizeIsDefined) + return S_FALSE; + *value = _currentSize; + return S_OK; +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h new file mode 100644 index 0000000..9a720c8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h @@ -0,0 +1,66 @@ +// 7z/FolderInStream.h + +#ifndef __7Z_FOLDERINSTREAM_H +#define __7Z_FOLDERINSTREAM_H + +#include "7zItem.h" +#include "7zHeader.h" + +#include "../IArchive.h" +#include "../Common/InStreamWithCRC.h" +#include "../../IStream.h" +#include "../../ICoder.h" + +namespace NArchive { +namespace N7z { + +class CFolderInStream: + public ISequentialInStream, + public ICompressGetSubStreamSize, + public CMyUnknownImp +{ +public: + + MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) + + CFolderInStream(); + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); +private: + CSequentialInStreamWithCRC *_inStreamWithHashSpec; + CMyComPtr _inStreamWithHash; + CMyComPtr _updateCallback; + + bool _currentSizeIsDefined; + UInt64 _currentSize; + + bool _fileIsOpen; + UInt64 _filePos; + + const UInt32 *_fileIndices; + UInt32 _numFiles; + UInt32 _fileIndex; + + HRESULT OpenStream(); + HRESULT CloseStream(); + void AddDigest(); +public: + void Init(IArchiveUpdateCallback *updateCallback, + const UInt32 *fileIndices, UInt32 numFiles); + CRecordVector Processed; + CRecordVector CRCs; + CRecordVector Sizes; + UInt64 GetFullSize() const + { + UInt64 size = 0; + for (int i = 0; i < Sizes.Size(); i++) + size += Sizes[i]; + return size; + } +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.cpp new file mode 100644 index 0000000..6206ffe --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.cpp @@ -0,0 +1,165 @@ +// 7zFolderOutStream.cpp + +#include "StdAfx.h" + +#include "7zFolderOutStream.h" + +namespace NArchive { +namespace N7z { + +CFolderOutStream::CFolderOutStream() +{ + _outStreamWithHashSpec = new COutStreamWithCRC; + _outStreamWithHash = _outStreamWithHashSpec; +} + +HRESULT CFolderOutStream::Init( + const CArchiveDatabaseEx *archiveDatabase, + UInt32 ref2Offset, + UInt32 startIndex, + const CBoolVector *extractStatuses, + IArchiveExtractCallback *extractCallback, + bool testMode, + bool checkCrc) +{ + _archiveDatabase = archiveDatabase; + _ref2Offset = ref2Offset; + _startIndex = startIndex; + + _extractStatuses = extractStatuses; + _extractCallback = extractCallback; + _testMode = testMode; + + _checkCrc = checkCrc; + + _currentIndex = 0; + _fileIsOpen = false; + return WriteEmptyFiles(); +} + +HRESULT CFolderOutStream::OpenFile() +{ + Int32 askMode; + if((*_extractStatuses)[_currentIndex]) + askMode = _testMode ? + NArchive::NExtract::NAskMode::kTest : + NArchive::NExtract::NAskMode::kExtract; + else + askMode = NArchive::NExtract::NAskMode::kSkip; + CMyComPtr realOutStream; + + UInt32 index = _startIndex + _currentIndex; + RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode)); + + _outStreamWithHashSpec->SetStream(realOutStream); + _outStreamWithHashSpec->Init(_checkCrc); + if (askMode == NArchive::NExtract::NAskMode::kExtract && + (!realOutStream)) + { + const CFileItem &fileInfo = _archiveDatabase->Files[index]; + if (!fileInfo.IsAnti && !fileInfo.IsDirectory) + askMode = NArchive::NExtract::NAskMode::kSkip; + } + return _extractCallback->PrepareOperation(askMode); +} + +HRESULT CFolderOutStream::WriteEmptyFiles() +{ + for(;_currentIndex < _extractStatuses->Size(); _currentIndex++) + { + UInt32 index = _startIndex + _currentIndex; + const CFileItem &fileInfo = _archiveDatabase->Files[index]; + if (!fileInfo.IsAnti && !fileInfo.IsDirectory && fileInfo.UnPackSize != 0) + return S_OK; + RINOK(OpenFile()); + RINOK(_extractCallback->SetOperationResult( + NArchive::NExtract::NOperationResult::kOK)); + _outStreamWithHashSpec->ReleaseStream(); + } + return S_OK; +} + +STDMETHODIMP CFolderOutStream::Write(const void *data, + UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize = 0; + while(_currentIndex < _extractStatuses->Size()) + { + if (_fileIsOpen) + { + UInt32 index = _startIndex + _currentIndex; + const CFileItem &fileInfo = _archiveDatabase->Files[index]; + UInt64 fileSize = fileInfo.UnPackSize; + + UInt32 numBytesToWrite = (UInt32)MyMin(fileSize - _filePos, + UInt64(size - realProcessedSize)); + + UInt32 processedSizeLocal; + RINOK(_outStreamWithHash->Write((const Byte *)data + realProcessedSize, + numBytesToWrite, &processedSizeLocal)); + + _filePos += processedSizeLocal; + realProcessedSize += processedSizeLocal; + if (_filePos == fileSize) + { + bool digestsAreEqual; + if (fileInfo.IsFileCRCDefined && _checkCrc) + digestsAreEqual = fileInfo.FileCRC == _outStreamWithHashSpec->GetCRC(); + else + digestsAreEqual = true; + + RINOK(_extractCallback->SetOperationResult( + digestsAreEqual ? + NArchive::NExtract::NOperationResult::kOK : + NArchive::NExtract::NOperationResult::kCRCError)); + _outStreamWithHashSpec->ReleaseStream(); + _fileIsOpen = false; + _currentIndex++; + } + if (realProcessedSize == size) + { + if (processedSize != NULL) + *processedSize = realProcessedSize; + return WriteEmptyFiles(); + } + } + else + { + RINOK(OpenFile()); + _fileIsOpen = true; + _filePos = 0; + } + } + if (processedSize != NULL) + *processedSize = size; + return S_OK; +} + +HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult) +{ + while(_currentIndex < _extractStatuses->Size()) + { + if (_fileIsOpen) + { + RINOK(_extractCallback->SetOperationResult(resultEOperationResult)); + _outStreamWithHashSpec->ReleaseStream(); + _fileIsOpen = false; + _currentIndex++; + } + else + { + RINOK(OpenFile()); + _fileIsOpen = true; + } + } + return S_OK; +} + +HRESULT CFolderOutStream::WasWritingFinished() +{ + if (_currentIndex == _extractStatuses->Size()) + return S_OK; + return E_FAIL; +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.h new file mode 100644 index 0000000..8ca91e6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.h @@ -0,0 +1,60 @@ +// 7zFolderOutStream.h + +#ifndef __7Z_FOLDEROUTSTREAM_H +#define __7Z_FOLDEROUTSTREAM_H + +#include "7zIn.h" + +#include "../../IStream.h" +#include "../IArchive.h" +#include "../Common/OutStreamWithCRC.h" + +namespace NArchive { +namespace N7z { + +class CFolderOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + CFolderOutStream(); + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +private: + + COutStreamWithCRC *_outStreamWithHashSpec; + CMyComPtr _outStreamWithHash; + const CArchiveDatabaseEx *_archiveDatabase; + const CBoolVector *_extractStatuses; + UInt32 _startIndex; + UInt32 _ref2Offset; + int _currentIndex; + // UInt64 _currentDataPos; + CMyComPtr _extractCallback; + bool _testMode; + + bool _fileIsOpen; + + bool _checkCrc; + UInt64 _filePos; + + HRESULT OpenFile(); + HRESULT WriteEmptyFiles(); +public: + HRESULT Init( + const CArchiveDatabaseEx *archiveDatabase, + UInt32 ref2Offset, + UInt32 startIndex, + const CBoolVector *extractStatuses, + IArchiveExtractCallback *extractCallback, + bool testMode, + bool checkCrc); + HRESULT FlushCorrupted(Int32 resultEOperationResult); + HRESULT WasWritingFinished(); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp new file mode 100644 index 0000000..bbef1ea --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp @@ -0,0 +1,793 @@ +// 7zHandler.cpp + +#include "StdAfx.h" + +#include "7zHandler.h" +#include "7zProperties.h" + +#include "../../../Common/IntToString.h" +#include "../../../Common/ComTry.h" +#include "../../../Windows/Defs.h" + +#include "../Common/ItemNameUtils.h" +#ifdef _7Z_VOL +#include "../Common/MultiStream.h" +#endif + +#ifdef __7Z_SET_PROPERTIES +#ifdef EXTRACT_ONLY +#include "../Common/ParseProperties.h" +#endif +#endif + +#ifdef COMPRESS_MT +#include "../../../Windows/System.h" +#endif + +using namespace NWindows; + +extern UString ConvertMethodIdToString(UInt64 id); + +namespace NArchive { +namespace N7z { + +CHandler::CHandler() +{ + _crcSize = 4; + + #ifdef EXTRACT_ONLY + #ifdef COMPRESS_MT + _numThreads = NWindows::NSystem::GetNumberOfProcessors(); + #endif + #else + Init(); + #endif +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = + #ifdef _7Z_VOL + _refs.Size(); + #else + *numItems = _database.Files.Size(); + #endif + return S_OK; +} + +#ifdef _SFX + +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */, + BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */) +{ + return E_NOTIMPL; +} + + +#else + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidSolid, VT_BOOL}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + UString resString; + CRecordVector ids; + int i; + for (i = 0; i < _database.Folders.Size(); i++) + { + const CFolder &f = _database.Folders[i]; + for (int j = f.Coders.Size() - 1; j >= 0; j--) + ids.AddToUniqueSorted(f.Coders[j].MethodID); + } + + for (i = 0; i < ids.Size(); i++) + { + UInt64 id = ids[i]; + UString methodName; + /* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName); + if (methodName.IsEmpty()) + methodName = ConvertMethodIdToString(id); + if (!resString.IsEmpty()) + resString += L' '; + resString += methodName; + } + prop = resString; + break; + } + case kpidSolid: prop = _database.IsSolid(); break; + case kpidNumBlocks: prop = (UInt32)_database.Folders.Size(); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +IMP_IInArchive_ArcProps + +#endif + +static void MySetFileTime(bool timeDefined, FILETIME unixTime, NWindows::NCOM::CPropVariant &prop) +{ + if (timeDefined) + prop = unixTime; +} + +#ifndef _SFX + +static UString ConvertUInt32ToString(UInt32 value) +{ + wchar_t buffer[32]; + ConvertUInt64ToString(value, buffer); + return buffer; +} + +static UString GetStringForSizeValue(UInt32 value) +{ + for (int i = 31; i >= 0; i--) + if ((UInt32(1) << i) == value) + return ConvertUInt32ToString(i); + UString result; + if (value % (1 << 20) == 0) + { + result += ConvertUInt32ToString(value >> 20); + result += L"m"; + } + else if (value % (1 << 10) == 0) + { + result += ConvertUInt32ToString(value >> 10); + result += L"k"; + } + else + { + result += ConvertUInt32ToString(value); + result += L"b"; + } + return result; +} + +static const UInt64 k_Copy = 0x0; +static const UInt64 k_LZMA = 0x030101; +static const UInt64 k_PPMD = 0x030401; + +static wchar_t GetHex(Byte value) +{ + return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10))); +} +static inline UString GetHex2(Byte value) +{ + UString result; + result += GetHex((Byte)(value >> 4)); + result += GetHex((Byte)(value & 0xF)); + return result; +} + +#endif + +static const UInt64 k_AES = 0x06F10701; + +#ifndef _SFX +static inline UInt32 GetUInt32FromMemLE(const Byte *p) +{ + return p[0] | (((UInt32)p[1]) << 8) | (((UInt32)p[2]) << 16) | (((UInt32)p[3]) << 24); +} +#endif + +bool CHandler::IsEncrypted(UInt32 index2) const +{ + CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + const CFolder &folderInfo = _database.Folders[folderIndex]; + for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--) + if (folderInfo.Coders[i].MethodID == k_AES) + return true; + } + return false; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + /* + const CRef2 &ref2 = _refs[index]; + if (ref2.Refs.IsEmpty()) + return E_FAIL; + const CRef &ref = ref2.Refs.Front(); + */ + + #ifdef _7Z_VOL + const CRef &ref = _refs[index]; + const CVolume &volume = _volumes[ref.VolumeIndex]; + const CArchiveDatabaseEx &_database = volume.Database; + UInt32 index2 = ref.ItemIndex; + const CFileItem &item = _database.Files[index2]; + #else + const CFileItem &item = _database.Files[index]; + UInt32 index2 = index; + #endif + + switch(propID) + { + case kpidPath: + { + if (!item.Name.IsEmpty()) + prop = NItemName::GetOSName(item.Name); + break; + } + case kpidIsFolder: + prop = item.IsDirectory; + break; + case kpidSize: + { + prop = item.UnPackSize; + // prop = ref2.UnPackSize; + break; + } + case kpidPosition: + { + /* + if (ref2.Refs.Size() > 1) + prop = ref2.StartPos; + else + */ + if (item.IsStartPosDefined) + prop = item.StartPos; + break; + } + case kpidPackedSize: + { + // prop = ref2.PackSize; + { + CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2) + prop = _database.GetFolderFullPackSize(folderIndex); + /* + else + prop = (UInt64)0; + */ + } + else + prop = (UInt64)0; + } + break; + } + case kpidLastAccessTime: + MySetFileTime(item.IsLastAccessTimeDefined, item.LastAccessTime, prop); + break; + case kpidCreationTime: + MySetFileTime(item.IsCreationTimeDefined, item.CreationTime, prop); + break; + case kpidLastWriteTime: + MySetFileTime(item.IsLastWriteTimeDefined, item.LastWriteTime, prop); + break; + case kpidAttributes: + if (item.AreAttributesDefined) + prop = item.Attributes; + break; + case kpidCRC: + if (item.IsFileCRCDefined) + prop = item.FileCRC; + break; + case kpidEncrypted: + { + prop = IsEncrypted(index2); + break; + } + #ifndef _SFX + case kpidMethod: + { + CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + const CFolder &folderInfo = _database.Folders[folderIndex]; + UString methodsString; + for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderInfo &coderInfo = folderInfo.Coders[i]; + if (!methodsString.IsEmpty()) + methodsString += L' '; + + { + UString methodName; + bool methodIsKnown = FindMethod( + EXTERNAL_CODECS_VARS + coderInfo.MethodID, methodName); + + if (methodIsKnown) + { + methodsString += methodName; + if (coderInfo.MethodID == k_LZMA) + { + if (coderInfo.Properties.GetCapacity() >= 5) + { + methodsString += L":"; + UInt32 dicSize = GetUInt32FromMemLE( + ((const Byte *)coderInfo.Properties + 1)); + methodsString += GetStringForSizeValue(dicSize); + } + } + else if (coderInfo.MethodID == k_PPMD) + { + if (coderInfo.Properties.GetCapacity() >= 5) + { + Byte order = *(const Byte *)coderInfo.Properties; + methodsString += L":o"; + methodsString += ConvertUInt32ToString(order); + methodsString += L":mem"; + UInt32 dicSize = GetUInt32FromMemLE( + ((const Byte *)coderInfo.Properties + 1)); + methodsString += GetStringForSizeValue(dicSize); + } + } + else if (coderInfo.MethodID == k_AES) + { + if (coderInfo.Properties.GetCapacity() >= 1) + { + methodsString += L":"; + const Byte *data = (const Byte *)coderInfo.Properties; + Byte firstByte = *data++; + UInt32 numCyclesPower = firstByte & 0x3F; + methodsString += ConvertUInt32ToString(numCyclesPower); + /* + if ((firstByte & 0xC0) != 0) + { + methodsString += L":"; + return S_OK; + UInt32 saltSize = (firstByte >> 7) & 1; + UInt32 ivSize = (firstByte >> 6) & 1; + if (coderInfo.Properties.GetCapacity() >= 2) + { + Byte secondByte = *data++; + saltSize += (secondByte >> 4); + ivSize += (secondByte & 0x0F); + } + } + */ + } + } + else + { + if (coderInfo.Properties.GetCapacity() > 0) + { + methodsString += L":["; + for (size_t bi = 0; bi < coderInfo.Properties.GetCapacity(); bi++) + { + if (bi > 5 && bi + 1 < coderInfo.Properties.GetCapacity()) + { + methodsString += L".."; + break; + } + else + methodsString += GetHex2(coderInfo.Properties[bi]); + } + methodsString += L"]"; + } + } + } + else + { + methodsString += ConvertMethodIdToString(coderInfo.MethodID); + } + } + } + prop = methodsString; + } + } + break; + case kpidBlock: + { + CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + prop = (UInt32)folderIndex; + } + break; + case kpidPackedSize0: + case kpidPackedSize1: + case kpidPackedSize2: + case kpidPackedSize3: + case kpidPackedSize4: + { + CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + const CFolder &folderInfo = _database.Folders[folderIndex]; + if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2 && + folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0)) + { + prop = _database.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0); + } + else + prop = (UInt64)0; + } + else + prop = (UInt64)0; + } + break; + #endif + case kpidIsAnti: + prop = item.IsAnti; + break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +#ifdef _7Z_VOL + +static const wchar_t *kExt = L"7z"; +static const wchar_t *kAfterPart = L".7z"; + +class CVolumeName +{ + bool _first; + UString _unchangedPart; + UString _changedPart; + UString _afterPart; +public: + bool InitName(const UString &name) + { + _first = true; + int dotPos = name.ReverseFind('.'); + UString basePart = name; + if (dotPos >= 0) + { + UString ext = name.Mid(dotPos + 1); + if (ext.CompareNoCase(kExt)==0 || + ext.CompareNoCase(L"EXE") == 0) + { + _afterPart = kAfterPart; + basePart = name.Left(dotPos); + } + } + + int numLetters = 1; + bool splitStyle = false; + if (basePart.Right(numLetters) == L"1") + { + while (numLetters < basePart.Length()) + { + if (basePart[basePart.Length() - numLetters - 1] != '0') + break; + numLetters++; + } + } + else + return false; + _unchangedPart = basePart.Left(basePart.Length() - numLetters); + _changedPart = basePart.Right(numLetters); + return true; + } + + UString GetNextName() + { + UString newName; + // if (_newStyle || !_first) + { + int i; + int numLetters = _changedPart.Length(); + for (i = numLetters - 1; i >= 0; i--) + { + wchar_t c = _changedPart[i]; + if (c == L'9') + { + c = L'0'; + newName = c + newName; + if (i == 0) + newName = UString(L'1') + newName; + continue; + } + c++; + newName = UString(c) + newName; + i--; + for (; i >= 0; i--) + newName = _changedPart[i] + newName; + break; + } + _changedPart = newName; + } + _first = false; + return _unchangedPart + _changedPart + _afterPart; + } +}; + +#endif + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + Close(); + #ifndef _SFX + _fileInfoPopIDs.Clear(); + #endif + try + { + CMyComPtr openArchiveCallbackTemp = openArchiveCallback; + #ifdef _7Z_VOL + CVolumeName seqName; + + CMyComPtr openVolumeCallback; + #endif + + #ifndef _NO_CRYPTO + CMyComPtr getTextPassword; + if (openArchiveCallback) + { + openArchiveCallbackTemp.QueryInterface( + IID_ICryptoGetTextPassword, &getTextPassword); + } + #endif + #ifdef _7Z_VOL + if (openArchiveCallback) + { + openArchiveCallbackTemp.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback); + } + for (;;) + { + CMyComPtr inStream; + if (!_volumes.IsEmpty()) + { + if (!openVolumeCallback) + break; + if(_volumes.Size() == 1) + { + UString baseName; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + break; + baseName = prop.bstrVal; + } + seqName.InitName(baseName); + } + + UString fullName = seqName.GetNextName(); + HRESULT result = openVolumeCallback->GetStream(fullName, &inStream); + if (result == S_FALSE) + break; + if (result != S_OK) + return result; + if (!stream) + break; + } + else + inStream = stream; + + CInArchive archive; + RINOK(archive.Open(inStream, maxCheckStartPosition)); + + _volumes.Add(CVolume()); + CVolume &volume = _volumes.Back(); + CArchiveDatabaseEx &database = volume.Database; + volume.Stream = inStream; + volume.StartRef2Index = _refs.Size(); + + HRESULT result = archive.ReadDatabase(database + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); + if (result != S_OK) + { + _volumes.Clear(); + return result; + } + database.Fill(); + for(int i = 0; i < database.Files.Size(); i++) + { + CRef refNew; + refNew.VolumeIndex = _volumes.Size() - 1; + refNew.ItemIndex = i; + _refs.Add(refNew); + /* + const CFileItem &file = database.Files[i]; + int j; + */ + /* + for (j = _refs.Size() - 1; j >= 0; j--) + { + CRef2 &ref2 = _refs[j]; + const CRef &ref = ref2.Refs.Back(); + const CVolume &volume2 = _volumes[ref.VolumeIndex]; + const CArchiveDatabaseEx &database2 = volume2.Database; + const CFileItem &file2 = database2.Files[ref.ItemIndex]; + if (file2.Name.CompareNoCase(file.Name) == 0) + { + if (!file.IsStartPosDefined) + continue; + if (file.StartPos != ref2.StartPos + ref2.UnPackSize) + continue; + ref2.Refs.Add(refNew); + break; + } + } + */ + /* + j = -1; + if (j < 0) + { + CRef2 ref2New; + ref2New.Refs.Add(refNew); + j = _refs.Add(ref2New); + } + CRef2 &ref2 = _refs[j]; + ref2.UnPackSize += file.UnPackSize; + ref2.PackSize += database.GetFilePackSize(i); + if (ref2.Refs.Size() == 1 && file.IsStartPosDefined) + ref2.StartPos = file.StartPos; + */ + } + if (database.Files.Size() != 1) + break; + const CFileItem &file = database.Files.Front(); + if (!file.IsStartPosDefined) + break; + } + #else + CInArchive archive; + RINOK(archive.Open(stream, maxCheckStartPosition)); + HRESULT result = archive.ReadDatabase( + EXTERNAL_CODECS_VARS + _database + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); + RINOK(result); + _database.Fill(); + _inStream = stream; + #endif + } + catch(...) + { + Close(); + return S_FALSE; + } + // _inStream = stream; + #ifndef _SFX + FillPopIDs(); + #endif + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + COM_TRY_BEGIN + #ifdef _7Z_VOL + _volumes.Clear(); + _refs.Clear(); + #else + _inStream.Release(); + _database.Clear(); + #endif + return S_OK; + COM_TRY_END +} + +#ifdef _7Z_VOL +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + if (index != 0) + return E_INVALIDARG; + *stream = 0; + CMultiStream *streamSpec = new CMultiStream; + CMyComPtr streamTemp = streamSpec; + + UInt64 pos = 0; + const UString *fileName; + for (int i = 0; i < _refs.Size(); i++) + { + const CRef &ref = _refs[i]; + const CVolume &volume = _volumes[ref.VolumeIndex]; + const CArchiveDatabaseEx &database = volume.Database; + const CFileItem &file = database.Files[ref.ItemIndex]; + if (i == 0) + fileName = &file.Name; + else + if (fileName->Compare(file.Name) != 0) + return S_FALSE; + if (!file.IsStartPosDefined) + return S_FALSE; + if (file.StartPos != pos) + return S_FALSE; + CNum folderIndex = database.FileIndexToFolderIndexMap[ref.ItemIndex]; + if (folderIndex == kNumNoIndex) + { + if (file.UnPackSize != 0) + return E_FAIL; + continue; + } + if (database.NumUnPackStreamsVector[folderIndex] != 1) + return S_FALSE; + const CFolder &folder = database.Folders[folderIndex]; + if (folder.Coders.Size() != 1) + return S_FALSE; + const CCoderInfo &coder = folder.Coders.Front(); + if (coder.NumInStreams != 1 || coder.NumOutStreams != 1) + return S_FALSE; + if (coder.MethodID != k_Copy) + return S_FALSE; + + pos += file.UnPackSize; + CMultiStream::CSubStreamInfo subStreamInfo; + subStreamInfo.Stream = volume.Stream; + subStreamInfo.Pos = database.GetFolderStreamPos(folderIndex, 0); + subStreamInfo.Size = file.UnPackSize; + streamSpec->Streams.Add(subStreamInfo); + } + streamSpec->Init(); + *stream = streamTemp.Detach(); + return S_OK; +} +#endif + + +#ifdef __7Z_SET_PROPERTIES +#ifdef EXTRACT_ONLY + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + COM_TRY_BEGIN + #ifdef COMPRESS_MT + const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); + _numThreads = numProcessors; + #endif + + for (int i = 0; i < numProperties; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + const PROPVARIANT &value = values[i]; + UInt32 number; + int index = ParseStringToUInt32(name, number); + if (index == 0) + { + if(name.Left(2).CompareNoCase(L"MT") == 0) + { + #ifdef COMPRESS_MT + RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads)); + #endif + continue; + } + else + return E_INVALIDARG; + } + } + return S_OK; + COM_TRY_END +} + +#endif +#endif + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.h new file mode 100644 index 0000000..ad4df41 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandler.h @@ -0,0 +1,146 @@ +// 7z/Handler.h + +#ifndef __7Z_HANDLER_H +#define __7Z_HANDLER_H + +#include "../../ICoder.h" +#include "../IArchive.h" +#include "7zIn.h" + +#include "7zCompressionMode.h" + +#include "../../Common/CreateCoder.h" + +#ifndef EXTRACT_ONLY +#include "../Common/HandlerOut.h" +#endif + +namespace NArchive { +namespace N7z { + +#ifdef _7Z_VOL +struct CRef +{ + int VolumeIndex; + int ItemIndex; +}; + +struct CVolume +{ + int StartRef2Index; + CMyComPtr Stream; + CArchiveDatabaseEx Database; +}; +#endif + +#ifndef __7Z_SET_PROPERTIES + +#ifdef EXTRACT_ONLY +#ifdef COMPRESS_MT +#define __7Z_SET_PROPERTIES +#endif +#else +#define __7Z_SET_PROPERTIES +#endif + +#endif + + +class CHandler: + #ifndef EXTRACT_ONLY + public NArchive::COutHandler, + #endif + public IInArchive, + #ifdef _7Z_VOL + public IInArchiveGetStream, + #endif + #ifdef __7Z_SET_PROPERTIES + public ISetProperties, + #endif + #ifndef EXTRACT_ONLY + public IOutArchive, + #endif + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + #ifdef _7Z_VOL + MY_QUERYINTERFACE_ENTRY(IInArchiveGetStream) + #endif + #ifdef __7Z_SET_PROPERTIES + MY_QUERYINTERFACE_ENTRY(ISetProperties) + #endif + #ifndef EXTRACT_ONLY + MY_QUERYINTERFACE_ENTRY(IOutArchive) + #endif + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + + #ifdef _7Z_VOL + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + #endif + + #ifdef __7Z_SET_PROPERTIES + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties); + #endif + + #ifndef EXTRACT_ONLY + INTERFACE_IOutArchive(;) + #endif + + DECL_ISetCompressCodecsInfo + + CHandler(); + +private: + #ifdef _7Z_VOL + CObjectVector _volumes; + CObjectVector _refs; + #else + CMyComPtr _inStream; + NArchive::N7z::CArchiveDatabaseEx _database; + #endif + + #ifdef EXTRACT_ONLY + + #ifdef COMPRESS_MT + UInt32 _numThreads; + #endif + + UInt32 _crcSize; + + #else + + CRecordVector _binds; + + HRESULT SetPassword(CCompressionMethodMode &methodMode, IArchiveUpdateCallback *updateCallback); + + HRESULT SetCompressionMethod(CCompressionMethodMode &method, + CObjectVector &methodsInfo + #ifdef COMPRESS_MT + , UInt32 numThreads + #endif + ); + + HRESULT SetCompressionMethod( + CCompressionMethodMode &method, + CCompressionMethodMode &headerMethod); + + #endif + + bool IsEncrypted(UInt32 index2) const; + #ifndef _SFX + + CRecordVector _fileInfoPopIDs; + void FillPopIDs(); + + #endif +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp new file mode 100644 index 0000000..af4b942 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp @@ -0,0 +1,464 @@ +// 7zHandlerOut.cpp + +#include "StdAfx.h" + +#include "7zHandler.h" +#include "7zOut.h" +#include "7zUpdate.h" + +#include "../../../Windows/PropVariant.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/StringToInt.h" +#include "../../IPassword.h" +#include "../../ICoder.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/ParseProperties.h" + +using namespace NWindows; + +namespace NArchive { +namespace N7z { + +static const wchar_t *kLZMAMethodName = L"LZMA"; +static const wchar_t *kCopyMethod = L"Copy"; +static const wchar_t *kDefaultMethodName = kLZMAMethodName; + +static const UInt32 kLzmaAlgorithmX5 = 1; +static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2"; +static const UInt32 kDictionaryForHeaders = 1 << 20; +static const UInt32 kNumFastBytesForHeaders = 273; +static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5; + +static inline bool IsCopyMethod(const UString &methodName) + { return (methodName.CompareNoCase(kCopyMethod) == 0); } + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) +{ + *type = NFileTimeType::kWindows; + return S_OK; +} + +HRESULT CHandler::SetPassword(CCompressionMethodMode &methodMode, + IArchiveUpdateCallback *updateCallback) +{ + CMyComPtr getTextPassword; + if (!getTextPassword) + { + CMyComPtr udateCallback2(updateCallback); + udateCallback2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword); + } + + if (getTextPassword) + { + CMyComBSTR password; + Int32 passwordIsDefined; + RINOK(getTextPassword->CryptoGetTextPassword2( + &passwordIsDefined, &password)); + methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); + if (methodMode.PasswordIsDefined) + methodMode.Password = password; + } + else + methodMode.PasswordIsDefined = false; + return S_OK; +} + +HRESULT CHandler::SetCompressionMethod( + CCompressionMethodMode &methodMode, + CCompressionMethodMode &headerMethod) +{ + HRESULT res = SetCompressionMethod(methodMode, _methods + #ifdef COMPRESS_MT + , _numThreads + #endif + ); + RINOK(res); + methodMode.Binds = _binds; + + if (_compressHeaders) + { + // headerMethod.Methods.Add(methodMode.Methods.Back()); + + CObjectVector headerMethodInfoVector; + COneMethodInfo oneMethodInfo; + oneMethodInfo.MethodName = kLZMAMethodName; + { + CProp property; + property.Id = NCoderPropID::kMatchFinder; + property.Value = kLzmaMatchFinderForHeaders; + oneMethodInfo.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kAlgorithm; + property.Value = kAlgorithmForHeaders; + oneMethodInfo.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kNumFastBytes; + property.Value = UInt32(kNumFastBytesForHeaders); + oneMethodInfo.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kDictionarySize; + property.Value = UInt32(kDictionaryForHeaders); + oneMethodInfo.Properties.Add(property); + } + headerMethodInfoVector.Add(oneMethodInfo); + HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector + #ifdef COMPRESS_MT + ,1 + #endif + ); + RINOK(res); + } + return S_OK; +} + +HRESULT CHandler::SetCompressionMethod( + CCompressionMethodMode &methodMode, + CObjectVector &methodsInfo + #ifdef COMPRESS_MT + , UInt32 numThreads + #endif + ) +{ + UInt32 level = _level; + + if (methodsInfo.IsEmpty()) + { + COneMethodInfo oneMethodInfo; + oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName); + methodsInfo.Add(oneMethodInfo); + } + + bool needSolid = false; + for(int i = 0; i < methodsInfo.Size(); i++) + { + COneMethodInfo &oneMethodInfo = methodsInfo[i]; + SetCompressionMethod2(oneMethodInfo + #ifdef COMPRESS_MT + , numThreads + #endif + ); + + if (!IsCopyMethod(oneMethodInfo.MethodName)) + needSolid = true; + + CMethodFull methodFull; + + if (!FindMethod( + EXTERNAL_CODECS_VARS + oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams)) + return E_INVALIDARG; + methodFull.Properties = oneMethodInfo.Properties; + methodMode.Methods.Add(methodFull); + + if (!_numSolidBytesDefined) + { + for (int j = 0; j < methodFull.Properties.Size(); j++) + { + const CProp &prop = methodFull.Properties[j]; + if ((prop.Id == NCoderPropID::kDictionarySize || + prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4) + { + _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7; + const UInt64 kMinSize = (1 << 24); + if (_numSolidBytes < kMinSize) + _numSolidBytes = kMinSize; + _numSolidBytesDefined = true; + break; + } + } + } + } + + if (!needSolid && !_numSolidBytesDefined) + { + _numSolidBytesDefined = true; + _numSolidBytes = 0; + } + return S_OK; +} + +static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, CArchiveFileTime &filetime, bool &filetimeIsDefined) +{ + filetimeIsDefined = false; + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(index, propID, &propVariant)); + if (propVariant.vt == VT_FILETIME) + { + filetime = propVariant.filetime; + filetimeIsDefined = true; + } + else if (propVariant.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + COM_TRY_BEGIN + + const CArchiveDatabaseEx *database = 0; + #ifdef _7Z_VOL + if(_volumes.Size() > 1) + return E_FAIL; + const CVolume *volume = 0; + if (_volumes.Size() == 1) + { + volume = &_volumes.Front(); + database = &volume->Database; + } + #else + if (_inStream != 0) + database = &_database; + #endif + + // CRecordVector compressStatuses; + CObjectVector updateItems; + // CRecordVector copyIndices; + + // CMyComPtr updateCallback2; + // updateCallback->QueryInterface(&updateCallback2); + + for(UInt32 i = 0; i < numItems; i++) + { + Int32 newData; + Int32 newProperties; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(i, + &newData, &newProperties, &indexInArchive)); + CUpdateItem updateItem; + updateItem.NewProperties = IntToBool(newProperties); + updateItem.NewData = IntToBool(newData); + updateItem.IndexInArchive = indexInArchive; + updateItem.IndexInClient = i; + updateItem.IsAnti = false; + updateItem.Size = 0; + + if (updateItem.IndexInArchive != -1) + { + const CFileItem &fileItem = database->Files[updateItem.IndexInArchive]; + updateItem.Name = fileItem.Name; + updateItem.IsDirectory = fileItem.IsDirectory; + updateItem.Size = fileItem.UnPackSize; + updateItem.IsAnti = fileItem.IsAnti; + + updateItem.CreationTime = fileItem.CreationTime; + updateItem.IsCreationTimeDefined = fileItem.IsCreationTimeDefined; + updateItem.LastWriteTime = fileItem.LastWriteTime; + updateItem.IsLastWriteTimeDefined = fileItem.IsLastWriteTimeDefined; + updateItem.LastAccessTime = fileItem.LastAccessTime; + updateItem.IsLastAccessTimeDefined = fileItem.IsLastAccessTimeDefined; + } + + if (updateItem.NewProperties) + { + bool nameIsDefined; + bool folderStatusIsDefined; + { + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant)); + if (propVariant.vt == VT_EMPTY) + updateItem.AttributesAreDefined = false; + else if (propVariant.vt != VT_UI4) + return E_INVALIDARG; + else + { + updateItem.Attributes = propVariant.ulVal; + updateItem.AttributesAreDefined = true; + } + } + + RINOK(GetTime(updateCallback, i, kpidCreationTime, updateItem.CreationTime, updateItem.IsCreationTimeDefined)); + RINOK(GetTime(updateCallback, i, kpidLastWriteTime, updateItem.LastWriteTime , updateItem.IsLastWriteTimeDefined)); + RINOK(GetTime(updateCallback, i, kpidLastAccessTime, updateItem.LastAccessTime, updateItem.IsLastAccessTimeDefined)); + + { + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(i, kpidPath, &propVariant)); + if (propVariant.vt == VT_EMPTY) + nameIsDefined = false; + else if (propVariant.vt != VT_BSTR) + return E_INVALIDARG; + else + { + updateItem.Name = NItemName::MakeLegalName(propVariant.bstrVal); + nameIsDefined = true; + } + } + { + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(i, kpidIsFolder, &propVariant)); + if (propVariant.vt == VT_EMPTY) + folderStatusIsDefined = false; + else if (propVariant.vt != VT_BOOL) + return E_INVALIDARG; + else + { + updateItem.IsDirectory = (propVariant.boolVal != VARIANT_FALSE); + folderStatusIsDefined = true; + } + } + + { + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(i, kpidIsAnti, &propVariant)); + if (propVariant.vt == VT_EMPTY) + updateItem.IsAnti = false; + else if (propVariant.vt != VT_BOOL) + return E_INVALIDARG; + else + updateItem.IsAnti = (propVariant.boolVal != VARIANT_FALSE); + } + + if (updateItem.IsAnti) + { + updateItem.AttributesAreDefined = false; + + updateItem.IsCreationTimeDefined = false; + updateItem.IsLastWriteTimeDefined = false; + updateItem.IsLastAccessTimeDefined = false; + + updateItem.Size = 0; + } + + if (!folderStatusIsDefined && updateItem.AttributesAreDefined) + updateItem.SetDirectoryStatusFromAttributes(); + } + + if (updateItem.NewData) + { + NCOM::CPropVariant propVariant; + RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant)); + if (propVariant.vt != VT_UI8) + return E_INVALIDARG; + updateItem.Size = (UInt64)propVariant.uhVal.QuadPart; + if (updateItem.Size != 0 && updateItem.IsAnti) + return E_INVALIDARG; + } + updateItems.Add(updateItem); + } + + CCompressionMethodMode methodMode, headerMethod; + RINOK(SetCompressionMethod(methodMode, headerMethod)); + #ifdef COMPRESS_MT + methodMode.NumThreads = _numThreads; + headerMethod.NumThreads = 1; + #endif + + RINOK(SetPassword(methodMode, updateCallback)); + + bool compressMainHeader = _compressHeaders; // check it + + if (methodMode.PasswordIsDefined) + { + compressMainHeader = true; + if(_encryptHeaders) + RINOK(SetPassword(headerMethod, updateCallback)); + } + + if (numItems < 2) + compressMainHeader = false; + + CUpdateOptions options; + options.Method = &methodMode; + options.HeaderMethod = (_compressHeaders || + (methodMode.PasswordIsDefined && _encryptHeaders)) ? + &headerMethod : 0; + options.UseFilters = _level != 0 && _autoFilter; + options.MaxFilter = _level >= 8; + + options.HeaderOptions.CompressMainHeader = compressMainHeader; + options.HeaderOptions.WriteModified = WriteModified; + options.HeaderOptions.WriteCreated = WriteCreated; + options.HeaderOptions.WriteAccessed = WriteAccessed; + + options.NumSolidFiles = _numSolidFiles; + options.NumSolidBytes = _numSolidBytes; + options.SolidExtension = _solidExtension; + options.RemoveSfxBlock = _removeSfxBlock; + options.VolumeMode = _volumeMode; + return Update( + EXTERNAL_CODECS_VARS + #ifdef _7Z_VOL + volume ? volume->Stream: 0, + volume ? database: 0, + #else + _inStream, + database, + #endif + updateItems, outStream, updateCallback, options); + COM_TRY_END +} + +static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream) +{ + stream = 0; + int index = ParseStringToUInt32(srcString, coder); + if (index == 0) + return E_INVALIDARG; + srcString.Delete(0, index); + if (srcString[0] == 'S') + { + srcString.Delete(0); + int index = ParseStringToUInt32(srcString, stream); + if (index == 0) + return E_INVALIDARG; + srcString.Delete(0, index); + } + return S_OK; +} + +static HRESULT GetBindInfo(UString &srcString, CBind &bind) +{ + RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream)); + if (srcString[0] != ':') + return E_INVALIDARG; + srcString.Delete(0); + RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream)); + if (!srcString.IsEmpty()) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + COM_TRY_BEGIN + _binds.Clear(); + BeforeSetProperty(); + + for (int i = 0; i < numProperties; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + + const PROPVARIANT &value = values[i]; + + if (name[0] == 'B') + { + name.Delete(0); + CBind bind; + RINOK(GetBindInfo(name, bind)); + _binds.Add(bind); + continue; + } + + RINOK(SetProperty(name, value)); + } + + return S_OK; + COM_TRY_END +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.cpp new file mode 100644 index 0000000..425231f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.cpp @@ -0,0 +1,27 @@ +// 7z/Header.cpp + +#include "StdAfx.h" +#include "7zHeader.h" + +namespace NArchive { +namespace N7z { + +Byte kSignature[kSignatureSize] = {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C}; +#ifdef _7Z_VOL +Byte kFinishSignature[kSignatureSize] = {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C + 1}; +#endif + +class SignatureInitializer +{ +public: + SignatureInitializer() + { + kSignature[0]--; + #ifdef _7Z_VOL + kFinishSignature[0]--; + #endif + }; +} g_SignatureInitializer; + +}} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.h new file mode 100644 index 0000000..e239ab2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zHeader.h @@ -0,0 +1,96 @@ +// 7z/7zHeader.h + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "../../../Common/Types.h" + +namespace NArchive { +namespace N7z { + +const int kSignatureSize = 6; +extern Byte kSignature[kSignatureSize]; + +// #define _7Z_VOL +// 7z-MultiVolume is not finished yet. +// It can work already, but I still do not like some +// things of that new multivolume format. +// So please keep it commented. + +#ifdef _7Z_VOL +extern Byte kFinishSignature[kSignatureSize]; +#endif + +struct CArchiveVersion +{ + Byte Major; + Byte Minor; +}; + +const Byte kMajorVersion = 0; + +struct CStartHeader +{ + UInt64 NextHeaderOffset; + UInt64 NextHeaderSize; + UInt32 NextHeaderCRC; +}; + +const UInt32 kStartHeaderSize = 20; + +#ifdef _7Z_VOL +struct CFinishHeader: public CStartHeader +{ + UInt64 ArchiveStartOffset; // data offset from end if that struct + UInt64 AdditionalStartBlockSize; // start signature & start header size +}; + +const UInt32 kFinishHeaderSize = kStartHeaderSize + 16; +#endif + +namespace NID +{ + enum EEnum + { + kEnd, + + kHeader, + + kArchiveProperties, + + kAdditionalStreamsInfo, + kMainStreamsInfo, + kFilesInfo, + + kPackInfo, + kUnPackInfo, + kSubStreamsInfo, + + kSize, + kCRC, + + kFolder, + + kCodersUnPackSize, + kNumUnPackStream, + + kEmptyStream, + kEmptyFile, + kAnti, + + kName, + kCreationTime, + kLastAccessTime, + kLastWriteTime, + kWinAttributes, + kComment, + + kEncodedHeader, + + kStartPos + }; +} + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.cpp new file mode 100644 index 0000000..a429254 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.cpp @@ -0,0 +1,1206 @@ +// 7zIn.cpp + +#include "StdAfx.h" + +#include "7zIn.h" +#include "7zDecode.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" +extern "C" +{ +#include "../../../../C/7zCrc.h" +} + +// define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader +#ifndef _SFX +#define FORMAT_7Z_RECOVERY +#endif + +namespace NArchive { +namespace N7z { + +class CInArchiveException {}; + +static void ThrowException() { throw CInArchiveException(); } +static inline void ThrowEndOfData() { ThrowException(); } +static inline void ThrowUnsupported() { ThrowException(); } +static inline void ThrowIncorrect() { ThrowException(); } +static inline void ThrowUnsupportedVersion() { ThrowException(); } + +/* +class CInArchiveException +{ +public: + enum CCauseType + { + kUnsupportedVersion = 0, + kUnsupported, + kIncorrect, + kEndOfData, + } Cause; + CInArchiveException(CCauseType cause): Cause(cause) {}; +}; + +static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); } +static void ThrowEndOfData() { ThrowException(CInArchiveException::kEndOfData); } +static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); } +static void ThrowIncorrect() { ThrowException(CInArchiveException::kIncorrect); } +static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); } +*/ + +class CStreamSwitch +{ + CInArchive *_archive; + bool _needRemove; +public: + CStreamSwitch(): _needRemove(false) {} + ~CStreamSwitch() { Remove(); } + void Remove(); + void Set(CInArchive *archive, const Byte *data, size_t size); + void Set(CInArchive *archive, const CByteBuffer &byteBuffer); + void Set(CInArchive *archive, const CObjectVector *dataVector); +}; + +void CStreamSwitch::Remove() +{ + if (_needRemove) + { + _archive->DeleteByteStream(); + _needRemove = false; + } +} + +void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size) +{ + Remove(); + _archive = archive; + _archive->AddByteStream(data, size); + _needRemove = true; +} + +void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) +{ + Set(archive, byteBuffer, byteBuffer.GetCapacity()); +} + +void CStreamSwitch::Set(CInArchive *archive, const CObjectVector *dataVector) +{ + Remove(); + Byte external = archive->ReadByte(); + if (external != 0) + { + int dataIndex = (int)archive->ReadNum(); + if (dataIndex < 0 || dataIndex >= dataVector->Size()) + ThrowIncorrect(); + Set(archive, (*dataVector)[dataIndex]); + } +} + +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__) +#define SZ_LITTLE_ENDIAN_UNALIGN +#endif + +#ifdef SZ_LITTLE_ENDIAN_UNALIGN +static inline UInt16 GetUInt16FromMem(const Byte *p) { return *(const UInt16 *)p; } +static inline UInt32 GetUInt32FromMem(const Byte *p) { return *(const UInt32 *)p; } +static inline UInt64 GetUInt64FromMem(const Byte *p) { return *(const UInt64 *)p; } +#else +static inline UInt16 GetUInt16FromMem(const Byte *p) { return p[0] | ((UInt16)p[1] << 8); } +static inline UInt32 GetUInt32FromMem(const Byte *p) { return p[0] | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16) | ((UInt32)p[3] << 24); } +static inline UInt64 GetUInt64FromMem(const Byte *p) { return GetUInt32FromMem(p) | ((UInt64)GetUInt32FromMem(p + 4) << 32); } +#endif + +Byte CInByte2::ReadByte() +{ + if (_pos >= _size) + ThrowEndOfData(); + return _buffer[_pos++]; +} + +void CInByte2::ReadBytes(Byte *data, size_t size) +{ + if (size > _size - _pos) + ThrowEndOfData(); + for (size_t i = 0; i < size; i++) + data[i] = _buffer[_pos++]; +} + +void CInByte2::SkeepData(UInt64 size) +{ + if (size > _size - _pos) + ThrowEndOfData(); +} + +void CInByte2::SkeepData() +{ + SkeepData(ReadNumber()); +} + +UInt64 CInByte2::ReadNumber() +{ + if (_pos >= _size) + ThrowEndOfData(); + Byte firstByte = _buffer[_pos++]; + Byte mask = 0x80; + UInt64 value = 0; + for (int i = 0; i < 8; i++) + { + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + value += (highPart << (i * 8)); + return value; + } + if (_pos >= _size) + ThrowEndOfData(); + value |= ((UInt64)_buffer[_pos++] << (8 * i)); + mask >>= 1; + } + return value; +} + +CNum CInByte2::ReadNum() +{ + UInt64 value = ReadNumber(); + if (value > kNumMax) + ThrowUnsupported(); + return (CNum)value; +} + +UInt32 CInByte2::ReadUInt32() +{ + if (_pos + 4 > _size) + ThrowEndOfData(); + UInt32 res = GetUInt32FromMem(_buffer + _pos); + _pos += 4; + return res; +} + +UInt64 CInByte2::ReadUInt64() +{ + if (_pos + 8 > _size) + ThrowEndOfData(); + UInt64 res = GetUInt64FromMem(_buffer + _pos); + _pos += 8; + return res; +} + +void CInByte2::ReadString(UString &s) +{ + const Byte *buf = _buffer + _pos; + size_t rem = (_size - _pos) / 2 * 2; + { + size_t i; + for (i = 0; i < rem; i += 2) + if (buf[i] == 0 && buf[i + 1] == 0) + break; + if (i == rem) + ThrowEndOfData(); + rem = i; + } + int len = (int)(rem / 2); + if (len < 0 || (size_t)len * 2 != rem) + ThrowUnsupported(); + wchar_t *p = s.GetBuffer(len); + int i; + for (i = 0; i < len; i++, buf += 2) + p[i] = (wchar_t)GetUInt16FromMem(buf); + p[i] = 0; + s.ReleaseBuffer(len); + _pos += rem + 2; +} + +static inline bool TestSignatureCandidate(const Byte *p) +{ + for (int i = 0; i < kSignatureSize; i++) + if (p[i] != kSignature[i]) + return false; + return (p[0x1A] == 0 && p[0x1B] == 0); +} + +HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + UInt32 processedSize; + RINOK(ReadStream(stream, _header, kHeaderSize, &processedSize)); + if (processedSize != kHeaderSize) + return S_FALSE; + if (TestSignatureCandidate(_header)) + return S_OK; + + CByteBuffer byteBuffer; + const UInt32 kBufferSize = (1 << 16); + byteBuffer.SetCapacity(kBufferSize); + Byte *buffer = byteBuffer; + UInt32 numPrevBytes = kHeaderSize - 1; + memcpy(buffer, _header + 1, numPrevBytes); + UInt64 curTestPos = _arhiveBeginStreamPosition + 1; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit) + break; + UInt32 numReadBytes = kBufferSize - numPrevBytes; + RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); + UInt32 numBytesInBuffer = numPrevBytes + processedSize; + if (numBytesInBuffer < kHeaderSize) + break; + UInt32 numTests = numBytesInBuffer - kHeaderSize + 1; + for(UInt32 pos = 0; pos < numTests; pos++, curTestPos++) + { + if (TestSignatureCandidate(buffer + pos)) + { + memcpy(_header, buffer + pos, kHeaderSize); + _arhiveBeginStreamPosition = curTestPos; + return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL); + } + } + numPrevBytes = numBytesInBuffer - numTests; + memmove(buffer, buffer + numTests, numPrevBytes); + } + return S_FALSE; +} + +// S_FALSE means that file is not archive +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) + RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); + _stream = stream; + return S_OK; +} + +void CInArchive::Close() +{ + _stream.Release(); +} + +void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) +{ + for (;;) + { + if (ReadID() == NID::kEnd) + break; + SkeepData(); + } +} + +void CInArchive::GetNextFolderItem(CFolder &folder) +{ + CNum numCoders = ReadNum(); + + folder.Coders.Clear(); + folder.Coders.Reserve((int)numCoders); + CNum numInStreams = 0; + CNum numOutStreams = 0; + CNum i; + for (i = 0; i < numCoders; i++) + { + folder.Coders.Add(CCoderInfo()); + CCoderInfo &coder = folder.Coders.Back(); + + { + Byte mainByte = ReadByte(); + int idSize = (mainByte & 0xF); + Byte longID[15]; + ReadBytes(longID, idSize); + if (idSize > 8) + ThrowUnsupported(); + UInt64 id = 0; + for (int j = 0; j < idSize; j++) + id |= (UInt64)longID[idSize - 1 - j] << (8 * j); + coder.MethodID = id; + + if ((mainByte & 0x10) != 0) + { + coder.NumInStreams = ReadNum(); + coder.NumOutStreams = ReadNum(); + } + else + { + coder.NumInStreams = 1; + coder.NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + CNum propertiesSize = ReadNum(); + coder.Properties.SetCapacity((size_t)propertiesSize); + ReadBytes((Byte *)coder.Properties, (size_t)propertiesSize); + } + if ((mainByte & 0x80) != 0) + ThrowUnsupported(); + } + numInStreams += coder.NumInStreams; + numOutStreams += coder.NumOutStreams; + } + + CNum numBindPairs; + numBindPairs = numOutStreams - 1; + folder.BindPairs.Clear(); + folder.BindPairs.Reserve(numBindPairs); + for (i = 0; i < numBindPairs; i++) + { + CBindPair bindPair; + bindPair.InIndex = ReadNum(); + bindPair.OutIndex = ReadNum(); + folder.BindPairs.Add(bindPair); + } + + CNum numPackedStreams = numInStreams - numBindPairs; + folder.PackStreams.Reserve(numPackedStreams); + if (numPackedStreams == 1) + { + for (CNum j = 0; j < numInStreams; j++) + if (folder.FindBindPairForInStream(j) < 0) + { + folder.PackStreams.Add(j); + break; + } + } + else + for(i = 0; i < numPackedStreams; i++) + folder.PackStreams.Add(ReadNum()); +} + +void CInArchive::WaitAttribute(UInt64 attribute) +{ + for (;;) + { + UInt64 type = ReadID(); + if (type == attribute) + return; + if (type == NID::kEnd) + ThrowIncorrect(); + SkeepData(); + } +} + +void CInArchive::ReadHashDigests(int numItems, + CRecordVector &digestsDefined, + CRecordVector &digests) +{ + ReadBoolVector2(numItems, digestsDefined); + digests.Clear(); + digests.Reserve(numItems); + for(int i = 0; i < numItems; i++) + { + UInt32 crc = 0; + if (digestsDefined[i]) + crc = ReadUInt32(); + digests.Add(crc); + } +} + +void CInArchive::ReadPackInfo( + UInt64 &dataOffset, + CRecordVector &packSizes, + CRecordVector &packCRCsDefined, + CRecordVector &packCRCs) +{ + dataOffset = ReadNumber(); + CNum numPackStreams = ReadNum(); + + WaitAttribute(NID::kSize); + packSizes.Clear(); + packSizes.Reserve(numPackStreams); + for (CNum i = 0; i < numPackStreams; i++) + packSizes.Add(ReadNumber()); + + UInt64 type; + for (;;) + { + type = ReadID(); + if (type == NID::kEnd) + break; + if (type == NID::kCRC) + { + ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs); + continue; + } + SkeepData(); + } + if (packCRCsDefined.IsEmpty()) + { + packCRCsDefined.Reserve(numPackStreams); + packCRCsDefined.Clear(); + packCRCs.Reserve(numPackStreams); + packCRCs.Clear(); + for(CNum i = 0; i < numPackStreams; i++) + { + packCRCsDefined.Add(false); + packCRCs.Add(0); + } + } +} + +void CInArchive::ReadUnPackInfo( + const CObjectVector *dataVector, + CObjectVector &folders) +{ + WaitAttribute(NID::kFolder); + CNum numFolders = ReadNum(); + + { + CStreamSwitch streamSwitch; + streamSwitch.Set(this, dataVector); + folders.Clear(); + folders.Reserve(numFolders); + for(CNum i = 0; i < numFolders; i++) + { + folders.Add(CFolder()); + GetNextFolderItem(folders.Back()); + } + } + + WaitAttribute(NID::kCodersUnPackSize); + + CNum i; + for (i = 0; i < numFolders; i++) + { + CFolder &folder = folders[i]; + CNum numOutStreams = folder.GetNumOutStreams(); + folder.UnPackSizes.Reserve(numOutStreams); + for (CNum j = 0; j < numOutStreams; j++) + folder.UnPackSizes.Add(ReadNumber()); + } + + for (;;) + { + UInt64 type = ReadID(); + if (type == NID::kEnd) + return; + if (type == NID::kCRC) + { + CRecordVector crcsDefined; + CRecordVector crcs; + ReadHashDigests(numFolders, crcsDefined, crcs); + for(i = 0; i < numFolders; i++) + { + CFolder &folder = folders[i]; + folder.UnPackCRCDefined = crcsDefined[i]; + folder.UnPackCRC = crcs[i]; + } + continue; + } + SkeepData(); + } +} + +void CInArchive::ReadSubStreamsInfo( + const CObjectVector &folders, + CRecordVector &numUnPackStreamsInFolders, + CRecordVector &unPackSizes, + CRecordVector &digestsDefined, + CRecordVector &digests) +{ + numUnPackStreamsInFolders.Clear(); + numUnPackStreamsInFolders.Reserve(folders.Size()); + UInt64 type; + for (;;) + { + type = ReadID(); + if (type == NID::kNumUnPackStream) + { + for(int i = 0; i < folders.Size(); i++) + numUnPackStreamsInFolders.Add(ReadNum()); + continue; + } + if (type == NID::kCRC || type == NID::kSize) + break; + if (type == NID::kEnd) + break; + SkeepData(); + } + + if (numUnPackStreamsInFolders.IsEmpty()) + for(int i = 0; i < folders.Size(); i++) + numUnPackStreamsInFolders.Add(1); + + int i; + for(i = 0; i < numUnPackStreamsInFolders.Size(); i++) + { + // v3.13 incorrectly worked with empty folders + // v4.07: we check that folder is empty + CNum numSubstreams = numUnPackStreamsInFolders[i]; + if (numSubstreams == 0) + continue; + UInt64 sum = 0; + for (CNum j = 1; j < numSubstreams; j++) + if (type == NID::kSize) + { + UInt64 size = ReadNumber(); + unPackSizes.Add(size); + sum += size; + } + unPackSizes.Add(folders[i].GetUnPackSize() - sum); + } + if (type == NID::kSize) + type = ReadID(); + + int numDigests = 0; + int numDigestsTotal = 0; + for(i = 0; i < folders.Size(); i++) + { + CNum numSubstreams = numUnPackStreamsInFolders[i]; + if (numSubstreams != 1 || !folders[i].UnPackCRCDefined) + numDigests += numSubstreams; + numDigestsTotal += numSubstreams; + } + + for (;;) + { + if (type == NID::kCRC) + { + CRecordVector digestsDefined2; + CRecordVector digests2; + ReadHashDigests(numDigests, digestsDefined2, digests2); + int digestIndex = 0; + for (i = 0; i < folders.Size(); i++) + { + CNum numSubstreams = numUnPackStreamsInFolders[i]; + const CFolder &folder = folders[i]; + if (numSubstreams == 1 && folder.UnPackCRCDefined) + { + digestsDefined.Add(true); + digests.Add(folder.UnPackCRC); + } + else + for (CNum j = 0; j < numSubstreams; j++, digestIndex++) + { + digestsDefined.Add(digestsDefined2[digestIndex]); + digests.Add(digests2[digestIndex]); + } + } + } + else if (type == NID::kEnd) + { + if (digestsDefined.IsEmpty()) + { + digestsDefined.Clear(); + digests.Clear(); + for (int i = 0; i < numDigestsTotal; i++) + { + digestsDefined.Add(false); + digests.Add(0); + } + } + return; + } + else + SkeepData(); + type = ReadID(); + } +} + +void CInArchive::ReadStreamsInfo( + const CObjectVector *dataVector, + UInt64 &dataOffset, + CRecordVector &packSizes, + CRecordVector &packCRCsDefined, + CRecordVector &packCRCs, + CObjectVector &folders, + CRecordVector &numUnPackStreamsInFolders, + CRecordVector &unPackSizes, + CRecordVector &digestsDefined, + CRecordVector &digests) +{ + for (;;) + { + UInt64 type = ReadID(); + if (type > ((UInt32)1 << 30)) + ThrowIncorrect(); + switch((UInt32)type) + { + case NID::kEnd: + return; + case NID::kPackInfo: + { + ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs); + break; + } + case NID::kUnPackInfo: + { + ReadUnPackInfo(dataVector, folders); + break; + } + case NID::kSubStreamsInfo: + { + ReadSubStreamsInfo(folders, numUnPackStreamsInFolders, + unPackSizes, digestsDefined, digests); + break; + } + default: + ThrowIncorrect(); + } + } +} + +void CInArchive::ReadBoolVector(int numItems, CBoolVector &v) +{ + v.Clear(); + v.Reserve(numItems); + Byte b = 0; + Byte mask = 0; + for(int i = 0; i < numItems; i++) + { + if (mask == 0) + { + b = ReadByte(); + mask = 0x80; + } + v.Add((b & mask) != 0); + mask >>= 1; + } +} + +void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v) +{ + Byte allAreDefined = ReadByte(); + if (allAreDefined == 0) + { + ReadBoolVector(numItems, v); + return; + } + v.Clear(); + v.Reserve(numItems); + for (int i = 0; i < numItems; i++) + v.Add(true); +} + +void CInArchive::ReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt32 type) +{ + CBoolVector boolVector; + ReadBoolVector2(files.Size(), boolVector); + + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + + for(int i = 0; i < files.Size(); i++) + { + CFileItem &file = files[i]; + CArchiveFileTime fileTime; + fileTime.dwLowDateTime = 0; + fileTime.dwHighDateTime = 0; + bool defined = boolVector[i]; + if (defined) + { + fileTime.dwLowDateTime = ReadUInt32(); + fileTime.dwHighDateTime = ReadUInt32(); + } + switch(type) + { + case NID::kCreationTime: + file.IsCreationTimeDefined = defined; + if (defined) + file.CreationTime = fileTime; + break; + case NID::kLastWriteTime: + file.IsLastWriteTimeDefined = defined; + if (defined) + file.LastWriteTime = fileTime; + break; + case NID::kLastAccessTime: + file.IsLastAccessTimeDefined = defined; + if (defined) + file.LastAccessTime = fileTime; + break; + } + } +} + +HRESULT CInArchive::ReadAndDecodePackedStreams( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 baseOffset, + UInt64 &dataOffset, CObjectVector &dataVector + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + ) +{ + CRecordVector packSizes; + CRecordVector packCRCsDefined; + CRecordVector packCRCs; + CObjectVector folders; + + CRecordVector numUnPackStreamsInFolders; + CRecordVector unPackSizes; + CRecordVector digestsDefined; + CRecordVector digests; + + ReadStreamsInfo(NULL, + dataOffset, + packSizes, + packCRCsDefined, + packCRCs, + folders, + numUnPackStreamsInFolders, + unPackSizes, + digestsDefined, + digests); + + // database.ArchiveInfo.DataStartPosition2 += database.ArchiveInfo.StartPositionAfterHeader; + + CNum packIndex = 0; + CDecoder decoder( + #ifdef _ST_MODE + false + #else + true + #endif + ); + UInt64 dataStartPos = baseOffset + dataOffset; + for(int i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + dataVector.Add(CByteBuffer()); + CByteBuffer &data = dataVector.Back(); + UInt64 unPackSize64 = folder.GetUnPackSize(); + size_t unPackSize = (size_t)unPackSize64; + if (unPackSize != unPackSize64) + ThrowUnsupported(); + data.SetCapacity(unPackSize); + + CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2; + CMyComPtr outStream = outStreamSpec; + outStreamSpec->Init(data, unPackSize); + + HRESULT result = decoder.Decode( + EXTERNAL_CODECS_LOC_VARS + _stream, dataStartPos, + &packSizes[packIndex], folder, outStream, NULL + #ifndef _NO_CRYPTO + , getTextPassword + #endif + #ifdef COMPRESS_MT + , false, 1 + #endif + ); + RINOK(result); + + if (folder.UnPackCRCDefined) + if (CrcCalc(data, unPackSize) != folder.UnPackCRC) + ThrowIncorrect(); + for (int j = 0; j < folder.PackStreams.Size(); j++) + dataStartPos += packSizes[packIndex++]; + } + return S_OK; +} + +HRESULT CInArchive::ReadHeader( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + ) +{ + UInt64 type = ReadID(); + + if (type == NID::kArchiveProperties) + { + ReadArchiveProperties(database.ArchiveInfo); + type = ReadID(); + } + + CObjectVector dataVector; + + if (type == NID::kAdditionalStreamsInfo) + { + HRESULT result = ReadAndDecodePackedStreams( + EXTERNAL_CODECS_LOC_VARS + database.ArchiveInfo.StartPositionAfterHeader, + database.ArchiveInfo.DataStartPosition2, + dataVector + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); + RINOK(result); + database.ArchiveInfo.DataStartPosition2 += database.ArchiveInfo.StartPositionAfterHeader; + type = ReadID(); + } + + CRecordVector unPackSizes; + CRecordVector digestsDefined; + CRecordVector digests; + + if (type == NID::kMainStreamsInfo) + { + ReadStreamsInfo(&dataVector, + database.ArchiveInfo.DataStartPosition, + database.PackSizes, + database.PackCRCsDefined, + database.PackCRCs, + database.Folders, + database.NumUnPackStreamsVector, + unPackSizes, + digestsDefined, + digests); + database.ArchiveInfo.DataStartPosition += database.ArchiveInfo.StartPositionAfterHeader; + type = ReadID(); + } + else + { + for(int i = 0; i < database.Folders.Size(); i++) + { + database.NumUnPackStreamsVector.Add(1); + CFolder &folder = database.Folders[i]; + unPackSizes.Add(folder.GetUnPackSize()); + digestsDefined.Add(folder.UnPackCRCDefined); + digests.Add(folder.UnPackCRC); + } + } + + database.Files.Clear(); + + if (type == NID::kEnd) + return S_OK; + if (type != NID::kFilesInfo) + ThrowIncorrect(); + + CNum numFiles = ReadNum(); + database.Files.Reserve(numFiles); + CNum i; + for(i = 0; i < numFiles; i++) + database.Files.Add(CFileItem()); + + database.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize); + if (!database.PackSizes.IsEmpty()) + database.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo); + if (numFiles > 0 && !digests.IsEmpty()) + database.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC); + + CBoolVector emptyStreamVector; + emptyStreamVector.Reserve((int)numFiles); + for(i = 0; i < numFiles; i++) + emptyStreamVector.Add(false); + CBoolVector emptyFileVector; + CBoolVector antiFileVector; + CNum numEmptyStreams = 0; + + for (;;) + { + UInt64 type = ReadID(); + if (type == NID::kEnd) + break; + UInt64 size = ReadNumber(); + bool isKnownType = true; + if (type > ((UInt32)1 << 30)) + isKnownType = false; + else switch((UInt32)type) + { + case NID::kName: + { + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + for(int i = 0; i < database.Files.Size(); i++) + _inByteBack->ReadString(database.Files[i].Name); + break; + } + case NID::kWinAttributes: + { + CBoolVector boolVector; + ReadBoolVector2(database.Files.Size(), boolVector); + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + for(i = 0; i < numFiles; i++) + { + CFileItem &file = database.Files[i]; + file.AreAttributesDefined = boolVector[i]; + if (file.AreAttributesDefined) + file.Attributes = ReadUInt32(); + } + break; + } + case NID::kStartPos: + { + CBoolVector boolVector; + ReadBoolVector2(database.Files.Size(), boolVector); + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + for(i = 0; i < numFiles; i++) + { + CFileItem &file = database.Files[i]; + file.IsStartPosDefined = boolVector[i]; + if (file.IsStartPosDefined) + file.StartPos = ReadUInt64(); + } + break; + } + case NID::kEmptyStream: + { + ReadBoolVector(numFiles, emptyStreamVector); + for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) + if (emptyStreamVector[i]) + numEmptyStreams++; + emptyFileVector.Reserve(numEmptyStreams); + antiFileVector.Reserve(numEmptyStreams); + for (i = 0; i < numEmptyStreams; i++) + { + emptyFileVector.Add(false); + antiFileVector.Add(false); + } + break; + } + case NID::kEmptyFile: + { + ReadBoolVector(numEmptyStreams, emptyFileVector); + break; + } + case NID::kAnti: + { + ReadBoolVector(numEmptyStreams, antiFileVector); + break; + } + case NID::kCreationTime: + case NID::kLastWriteTime: + case NID::kLastAccessTime: + { + ReadTime(dataVector, database.Files, (UInt32)type); + break; + } + default: + isKnownType = false; + } + if (isKnownType) + database.ArchiveInfo.FileInfoPopIDs.Add(type); + else + SkeepData(size); + } + + CNum emptyFileIndex = 0; + CNum sizeIndex = 0; + for(i = 0; i < numFiles; i++) + { + CFileItem &file = database.Files[i]; + file.HasStream = !emptyStreamVector[i]; + if(file.HasStream) + { + file.IsDirectory = false; + file.IsAnti = false; + file.UnPackSize = unPackSizes[sizeIndex]; + file.FileCRC = digests[sizeIndex]; + file.IsFileCRCDefined = digestsDefined[sizeIndex]; + sizeIndex++; + } + else + { + file.IsDirectory = !emptyFileVector[emptyFileIndex]; + file.IsAnti = antiFileVector[emptyFileIndex]; + emptyFileIndex++; + file.UnPackSize = 0; + file.IsFileCRCDefined = false; + } + } + return S_OK; +} + + +void CArchiveDatabaseEx::FillFolderStartPackStream() +{ + FolderStartPackStreamIndex.Clear(); + FolderStartPackStreamIndex.Reserve(Folders.Size()); + CNum startPos = 0; + for(int i = 0; i < Folders.Size(); i++) + { + FolderStartPackStreamIndex.Add(startPos); + startPos += (CNum)Folders[i].PackStreams.Size(); + } +} + +void CArchiveDatabaseEx::FillStartPos() +{ + PackStreamStartPositions.Clear(); + PackStreamStartPositions.Reserve(PackSizes.Size()); + UInt64 startPos = 0; + for(int i = 0; i < PackSizes.Size(); i++) + { + PackStreamStartPositions.Add(startPos); + startPos += PackSizes[i]; + } +} + +void CArchiveDatabaseEx::FillFolderStartFileIndex() +{ + FolderStartFileIndex.Clear(); + FolderStartFileIndex.Reserve(Folders.Size()); + FileIndexToFolderIndexMap.Clear(); + FileIndexToFolderIndexMap.Reserve(Files.Size()); + + int folderIndex = 0; + CNum indexInFolder = 0; + for (int i = 0; i < Files.Size(); i++) + { + const CFileItem &file = Files[i]; + bool emptyStream = !file.HasStream; + if (emptyStream && indexInFolder == 0) + { + FileIndexToFolderIndexMap.Add(kNumNoIndex); + continue; + } + if (indexInFolder == 0) + { + // v3.13 incorrectly worked with empty folders + // v4.07: Loop for skipping empty folders + for (;;) + { + if (folderIndex >= Folders.Size()) + ThrowIncorrect(); + FolderStartFileIndex.Add(i); // check it + if (NumUnPackStreamsVector[folderIndex] != 0) + break; + folderIndex++; + } + } + FileIndexToFolderIndexMap.Add(folderIndex); + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= NumUnPackStreamsVector[folderIndex]) + { + folderIndex++; + indexInFolder = 0; + } + } +} + +HRESULT CInArchive::ReadDatabase2( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + ) +{ + database.Clear(); + database.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; + + database.ArchiveInfo.Version.Major = _header[6]; + database.ArchiveInfo.Version.Minor = _header[7]; + + if (database.ArchiveInfo.Version.Major != kMajorVersion) + ThrowUnsupportedVersion(); + + UInt32 crcFromArchive = GetUInt32FromMem(_header + 8); + UInt64 nextHeaderOffset = GetUInt64FromMem(_header + 0xC); + UInt64 nextHeaderSize = GetUInt64FromMem(_header + 0x14); + UInt32 nextHeaderCRC = GetUInt32FromMem(_header + 0x1C); + UInt32 crc = CrcCalc(_header + 0xC, 20); + + #ifdef FORMAT_7Z_RECOVERY + if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) + { + UInt64 cur, cur2; + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); + const int kCheckSize = 500; + Byte buf[kCheckSize]; + RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2)); + int checkSize = kCheckSize; + if (cur2 - cur < kCheckSize) + checkSize = (int)(cur2 - cur); + RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2)); + + UInt32 realProcessedSize; + RINOK(_stream->Read(buf, (UInt32)kCheckSize, &realProcessedSize)); + + int i; + for (i = (int)realProcessedSize - 2; i >= 0; i--) + if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04) + break; + if (i < 0) + return S_FALSE; + nextHeaderSize = realProcessedSize - i; + nextHeaderOffset = cur2 - cur + i; + nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); + RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); + } + #endif + + #ifdef FORMAT_7Z_RECOVERY + crcFromArchive = crc; + #endif + + database.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; + + if (crc != crcFromArchive) + ThrowIncorrect(); + + if (nextHeaderSize == 0) + return S_OK; + + if (nextHeaderSize > (UInt64)0xFFFFFFFF) + return S_FALSE; + + RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); + + CByteBuffer buffer2; + buffer2.SetCapacity((size_t)nextHeaderSize); + + UInt32 realProcessedSize; + RINOK(_stream->Read(buffer2, (UInt32)nextHeaderSize, &realProcessedSize)); + if (realProcessedSize != (UInt32)nextHeaderSize) + return S_FALSE; + if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC) + ThrowIncorrect(); + + CStreamSwitch streamSwitch; + streamSwitch.Set(this, buffer2); + + CObjectVector dataVector; + + for (;;) + { + UInt64 type = ReadID(); + if (type == NID::kHeader) + break; + if (type != NID::kEncodedHeader) + ThrowIncorrect(); + HRESULT result = ReadAndDecodePackedStreams( + EXTERNAL_CODECS_LOC_VARS + database.ArchiveInfo.StartPositionAfterHeader, + database.ArchiveInfo.DataStartPosition2, + dataVector + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); + RINOK(result); + if (dataVector.Size() == 0) + return S_OK; + if (dataVector.Size() > 1) + ThrowIncorrect(); + streamSwitch.Remove(); + streamSwitch.Set(this, dataVector.Front()); + } + + return ReadHeader( + EXTERNAL_CODECS_LOC_VARS + database + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); +} + +HRESULT CInArchive::ReadDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + ) +{ + try + { + return ReadDatabase2( + EXTERNAL_CODECS_LOC_VARS database + #ifndef _NO_CRYPTO + , getTextPassword + #endif + ); + } + catch(CInArchiveException &) { return S_FALSE; } +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.h new file mode 100644 index 0000000..aae4350 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zIn.h @@ -0,0 +1,235 @@ +// 7zIn.h + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "../../../Common/MyCom.h" +#include "../../IStream.h" +#include "../../IPassword.h" +#include "../../Common/CreateCoder.h" +#include "../../Common/InBuffer.h" + +#include "7zItem.h" + +namespace NArchive { +namespace N7z { + +struct CInArchiveInfo +{ + CArchiveVersion Version; + UInt64 StartPosition; + UInt64 StartPositionAfterHeader; + UInt64 DataStartPosition; + UInt64 DataStartPosition2; + CRecordVector FileInfoPopIDs; + void Clear() + { + FileInfoPopIDs.Clear(); + } +}; + +struct CArchiveDatabaseEx: public CArchiveDatabase +{ + CInArchiveInfo ArchiveInfo; + CRecordVector PackStreamStartPositions; + CRecordVector FolderStartPackStreamIndex; + CRecordVector FolderStartFileIndex; + CRecordVector FileIndexToFolderIndexMap; + + void Clear() + { + CArchiveDatabase::Clear(); + ArchiveInfo.Clear(); + PackStreamStartPositions.Clear(); + FolderStartPackStreamIndex.Clear(); + FolderStartFileIndex.Clear(); + FileIndexToFolderIndexMap.Clear(); + } + + void FillFolderStartPackStream(); + void FillStartPos(); + void FillFolderStartFileIndex(); + + void Fill() + { + FillFolderStartPackStream(); + FillStartPos(); + FillFolderStartFileIndex(); + } + + UInt64 GetFolderStreamPos(int folderIndex, int indexInFolder) const + { + return ArchiveInfo.DataStartPosition + + PackStreamStartPositions[FolderStartPackStreamIndex[folderIndex] + indexInFolder]; + } + + UInt64 GetFolderFullPackSize(int folderIndex) const + { + CNum packStreamIndex = FolderStartPackStreamIndex[folderIndex]; + const CFolder &folder = Folders[folderIndex]; + UInt64 size = 0; + for (int i = 0; i < folder.PackStreams.Size(); i++) + size += PackSizes[packStreamIndex + i]; + return size; + } + + UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const + { + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; + } + + UInt64 GetFilePackSize(CNum fileIndex) const + { + CNum folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex != kNumNoIndex) + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + return 0; + } +}; + +class CInByte2 +{ + const Byte *_buffer; + size_t _size; + size_t _pos; +public: + void Init(const Byte *buffer, size_t size) + { + _buffer = buffer; + _size = size; + _pos = 0; + } + Byte ReadByte(); + void ReadBytes(Byte *data, size_t size); + void SkeepData(UInt64 size); + void SkeepData(); + UInt64 ReadNumber(); + CNum ReadNum(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + void ReadString(UString &s); +}; + +class CStreamSwitch; + +const UInt32 kHeaderSize = 32; + +class CInArchive +{ + friend class CStreamSwitch; + + CMyComPtr _stream; + + CObjectVector _inByteVector; + CInByte2 *_inByteBack; + + UInt64 _arhiveBeginStreamPosition; + + Byte _header[kHeaderSize]; + + void AddByteStream(const Byte *buffer, size_t size) + { + _inByteVector.Add(CInByte2()); + _inByteBack = &_inByteVector.Back(); + _inByteBack->Init(buffer, size); + } + + void DeleteByteStream() + { + _inByteVector.DeleteBack(); + if (!_inByteVector.IsEmpty()) + _inByteBack = &_inByteVector.Back(); + } + +private: + HRESULT FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + + void ReadBytes(Byte *data, size_t size) { _inByteBack->ReadBytes(data, size); } + Byte ReadByte() { return _inByteBack->ReadByte(); } + UInt64 ReadNumber() { return _inByteBack->ReadNumber(); } + CNum ReadNum() { return _inByteBack->ReadNum(); } + UInt64 ReadID() { return _inByteBack->ReadNumber(); } + UInt32 ReadUInt32() { return _inByteBack->ReadUInt32(); } + UInt64 ReadUInt64() { return _inByteBack->ReadUInt64(); } + void SkeepData(UInt64 size) { _inByteBack->SkeepData(size); } + void SkeepData() { _inByteBack->SkeepData(); } + void WaitAttribute(UInt64 attribute); + + void ReadArchiveProperties(CInArchiveInfo &archiveInfo); + void GetNextFolderItem(CFolder &itemInfo); + void ReadHashDigests(int numItems, + CRecordVector &digestsDefined, CRecordVector &digests); + + void ReadPackInfo( + UInt64 &dataOffset, + CRecordVector &packSizes, + CRecordVector &packCRCsDefined, + CRecordVector &packCRCs); + + void ReadUnPackInfo( + const CObjectVector *dataVector, + CObjectVector &folders); + + void ReadSubStreamsInfo( + const CObjectVector &folders, + CRecordVector &numUnPackStreamsInFolders, + CRecordVector &unPackSizes, + CRecordVector &digestsDefined, + CRecordVector &digests); + + void ReadStreamsInfo( + const CObjectVector *dataVector, + UInt64 &dataOffset, + CRecordVector &packSizes, + CRecordVector &packCRCsDefined, + CRecordVector &packCRCs, + CObjectVector &folders, + CRecordVector &numUnPackStreamsInFolders, + CRecordVector &unPackSizes, + CRecordVector &digestsDefined, + CRecordVector &digests); + + + void ReadBoolVector(int numItems, CBoolVector &v); + void ReadBoolVector2(int numItems, CBoolVector &v); + void ReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt32 type); + HRESULT ReadAndDecodePackedStreams( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 baseOffset, UInt64 &dataOffset, + CObjectVector &dataVector + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword + #endif + ); + HRESULT ReadHeader( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword + #endif + ); + HRESULT ReadDatabase2( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword + #endif + ); +public: + HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); // S_FALSE means is not archive + void Close(); + + HRESULT ReadDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &database + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword + #endif + ); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zItem.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zItem.h new file mode 100644 index 0000000..31c5ce2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zItem.h @@ -0,0 +1,184 @@ +// 7zItem.h + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "../../../Common/Buffer.h" +#include "../../../Common/MyString.h" +#include "../../Common/MethodId.h" +#include "7zHeader.h" + +namespace NArchive { +namespace N7z { + +typedef UInt32 CNum; +const CNum kNumMax = 0x7FFFFFFF; +const CNum kNumNoIndex = 0xFFFFFFFF; + +struct CCoderInfo +{ + CMethodId MethodID; + CByteBuffer Properties; + CNum NumInStreams; + CNum NumOutStreams; + bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } +}; + +struct CBindPair +{ + CNum InIndex; + CNum OutIndex; +}; + +struct CFolder +{ + CObjectVector Coders; + CRecordVector BindPairs; + CRecordVector PackStreams; + CRecordVector UnPackSizes; + UInt32 UnPackCRC; + bool UnPackCRCDefined; + + CFolder(): UnPackCRCDefined(false) {} + + UInt64 GetUnPackSize() const // test it + { + if (UnPackSizes.IsEmpty()) + return 0; + for (int i = UnPackSizes.Size() - 1; i >= 0; i--) + if (FindBindPairForOutStream(i) < 0) + return UnPackSizes[i]; + throw 1; + } + + CNum GetNumOutStreams() const + { + CNum result = 0; + for (int i = 0; i < Coders.Size(); i++) + result += Coders[i].NumOutStreams; + return result; + } + + int FindBindPairForInStream(CNum inStreamIndex) const + { + for(int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; + } + int FindBindPairForOutStream(CNum outStreamIndex) const + { + for(int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; + } + int FindPackStreamArrayIndex(CNum inStreamIndex) const + { + for(int i = 0; i < PackStreams.Size(); i++) + if (PackStreams[i] == inStreamIndex) + return i; + return -1; + } +}; + +typedef FILETIME CArchiveFileTime; + +class CFileItem +{ +public: + CArchiveFileTime CreationTime; + CArchiveFileTime LastWriteTime; + CArchiveFileTime LastAccessTime; + UInt64 UnPackSize; + UInt64 StartPos; + UInt32 Attributes; + UInt32 FileCRC; + UString Name; + + bool HasStream; // Test it !!! it means that there is + // stream in some folder. It can be empty stream + bool IsDirectory; + bool IsAnti; + bool IsFileCRCDefined; + bool AreAttributesDefined; + bool IsCreationTimeDefined; + bool IsLastWriteTimeDefined; + bool IsLastAccessTimeDefined; + bool IsStartPosDefined; + + /* + const bool HasStream() const { + return !IsDirectory && !IsAnti && UnPackSize != 0; } + */ + CFileItem(): + HasStream(true), + IsDirectory(false), + IsAnti(false), + IsFileCRCDefined(false), + AreAttributesDefined(false), + IsCreationTimeDefined(false), + IsLastWriteTimeDefined(false), + IsLastAccessTimeDefined(false), + IsStartPosDefined(false) + {} + void SetAttributes(UInt32 attributes) + { + AreAttributesDefined = true; + Attributes = attributes; + } + void SetCreationTime(const CArchiveFileTime &creationTime) + { + IsCreationTimeDefined = true; + CreationTime = creationTime; + } + void SetLastWriteTime(const CArchiveFileTime &lastWriteTime) + { + IsLastWriteTimeDefined = true; + LastWriteTime = lastWriteTime; + } + void SetLastAccessTime(const CArchiveFileTime &lastAccessTime) + { + IsLastAccessTimeDefined = true; + LastAccessTime = lastAccessTime; + } +}; + +struct CArchiveDatabase +{ + CRecordVector PackSizes; + CRecordVector PackCRCsDefined; + CRecordVector PackCRCs; + CObjectVector Folders; + CRecordVector NumUnPackStreamsVector; + CObjectVector Files; + void Clear() + { + PackSizes.Clear(); + PackCRCsDefined.Clear(); + PackCRCs.Clear(); + Folders.Clear(); + NumUnPackStreamsVector.Clear(); + Files.Clear(); + } + bool IsEmpty() const + { + return (PackSizes.IsEmpty() && + PackCRCsDefined.IsEmpty() && + PackCRCs.IsEmpty() && + Folders.IsEmpty() && + NumUnPackStreamsVector.IsEmpty() && + Files.IsEmpty()); + } + bool IsSolid() const + { + for (int i = 0; i < NumUnPackStreamsVector.Size(); i++) + if (NumUnPackStreamsVector[i] > 1) + return true; + return false; + } +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.cpp new file mode 100644 index 0000000..a00cdf5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.cpp @@ -0,0 +1,1026 @@ +// 7zOut.cpp + +#include "StdAfx.h" + +#include "../../../Common/AutoPtr.h" +#include "../../Common/StreamObjects.h" + +#include "7zOut.h" + +extern "C" +{ +#include "../../../../C/7zCrc.h" +} + +static HRESULT WriteBytes(ISequentialOutStream *stream, const void *data, size_t size) +{ + while (size > 0) + { + UInt32 curSize = (UInt32)MyMin(size, (size_t)0xFFFFFFFF); + UInt32 processedSize; + RINOK(stream->Write(data, curSize, &processedSize)); + if(processedSize == 0) + return E_FAIL; + data = (const void *)((const Byte *)data + processedSize); + size -= processedSize; + } + return S_OK; +} + +namespace NArchive { +namespace N7z { + +HRESULT COutArchive::WriteDirect(const void *data, UInt32 size) +{ + return ::WriteBytes(SeqStream, data, size); +} + +UInt32 CrcUpdateUInt32(UInt32 crc, UInt32 value) +{ + for (int i = 0; i < 4; i++, value >>= 8) + crc = CRC_UPDATE_BYTE(crc, (Byte)value); + return crc; +} + +UInt32 CrcUpdateUInt64(UInt32 crc, UInt64 value) +{ + for (int i = 0; i < 8; i++, value >>= 8) + crc = CRC_UPDATE_BYTE(crc, (Byte)value); + return crc; +} + +HRESULT COutArchive::WriteDirectUInt32(UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + RINOK(WriteDirectByte((Byte)value)); + value >>= 8; + } + return S_OK; +} + +HRESULT COutArchive::WriteDirectUInt64(UInt64 value) +{ + for (int i = 0; i < 8; i++) + { + RINOK(WriteDirectByte((Byte)value)); + value >>= 8; + } + return S_OK; +} + +HRESULT COutArchive::WriteSignature() +{ + RINOK(WriteDirect(kSignature, kSignatureSize)); + RINOK(WriteDirectByte(kMajorVersion)); + return WriteDirectByte(2); +} + +#ifdef _7Z_VOL +HRESULT COutArchive::WriteFinishSignature() +{ + RINOK(WriteDirect(kFinishSignature, kSignatureSize)); + CArchiveVersion av; + av.Major = kMajorVersion; + av.Minor = 2; + RINOK(WriteDirectByte(av.Major)); + return WriteDirectByte(av.Minor); +} +#endif + +HRESULT COutArchive::WriteStartHeader(const CStartHeader &h) +{ + UInt32 crc = CRC_INIT_VAL; + crc = CrcUpdateUInt64(crc, h.NextHeaderOffset); + crc = CrcUpdateUInt64(crc, h.NextHeaderSize); + crc = CrcUpdateUInt32(crc, h.NextHeaderCRC); + RINOK(WriteDirectUInt32(CRC_GET_DIGEST(crc))); + RINOK(WriteDirectUInt64(h.NextHeaderOffset)); + RINOK(WriteDirectUInt64(h.NextHeaderSize)); + return WriteDirectUInt32(h.NextHeaderCRC); +} + +#ifdef _7Z_VOL +HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h) +{ + CCRC crc; + crc.UpdateUInt64(h.NextHeaderOffset); + crc.UpdateUInt64(h.NextHeaderSize); + crc.UpdateUInt32(h.NextHeaderCRC); + crc.UpdateUInt64(h.ArchiveStartOffset); + crc.UpdateUInt64(h.AdditionalStartBlockSize); + RINOK(WriteDirectUInt32(crc.GetDigest())); + RINOK(WriteDirectUInt64(h.NextHeaderOffset)); + RINOK(WriteDirectUInt64(h.NextHeaderSize)); + RINOK(WriteDirectUInt32(h.NextHeaderCRC)); + RINOK(WriteDirectUInt64(h.ArchiveStartOffset)); + return WriteDirectUInt64(h.AdditionalStartBlockSize); +} +#endif + +HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker) +{ + Close(); + #ifdef _7Z_VOL + // endMarker = false; + _endMarker = endMarker; + #endif + SeqStream = stream; + if (!endMarker) + { + SeqStream.QueryInterface(IID_IOutStream, &Stream); + if (!Stream) + { + return E_NOTIMPL; + // endMarker = true; + } + } + #ifdef _7Z_VOL + if (endMarker) + { + /* + CStartHeader sh; + sh.NextHeaderOffset = (UInt32)(Int32)-1; + sh.NextHeaderSize = (UInt32)(Int32)-1; + sh.NextHeaderCRC = 0; + WriteStartHeader(sh); + */ + } + else + #endif + { + if (!Stream) + return E_FAIL; + RINOK(WriteSignature()); + RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos)); + } + return S_OK; +} + +void COutArchive::Close() +{ + SeqStream.Release(); + Stream.Release(); +} + +HRESULT COutArchive::SkeepPrefixArchiveHeader() +{ + #ifdef _7Z_VOL + if (_endMarker) + return S_OK; + #endif + return Stream->Seek(24, STREAM_SEEK_CUR, NULL); +} + +HRESULT COutArchive::WriteBytes(const void *data, size_t size) +{ + if (_mainMode) + { + if (_dynamicMode) + _dynamicBuffer.Write(data, size); + else + _outByte.WriteBytes(data, size); + _crc = CrcUpdate(_crc, data, size); + } + else + { + if (_countMode) + _countSize += size; + else + RINOK(_outByte2.Write(data, size)); + } + return S_OK; +} + +HRESULT COutArchive::WriteBytes(const CByteBuffer &data) +{ + return WriteBytes(data, data.GetCapacity()); +} + +HRESULT COutArchive::WriteByte(Byte b) +{ + return WriteBytes(&b, 1); +} + +HRESULT COutArchive::WriteUInt32(UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + RINOK(WriteByte((Byte)value)); + value >>= 8; + } + return S_OK; +} + +HRESULT COutArchive::WriteNumber(UInt64 value) +{ + Byte firstByte = 0; + Byte mask = 0x80; + int i; + for (i = 0; i < 8; i++) + { + if (value < ((UInt64(1) << ( 7 * (i + 1))))) + { + firstByte |= Byte(value >> (8 * i)); + break; + } + firstByte |= mask; + mask >>= 1; + } + RINOK(WriteByte(firstByte)); + for (;i > 0; i--) + { + RINOK(WriteByte((Byte)value)); + value >>= 8; + } + return S_OK; +} + +#ifdef _7Z_VOL +static UInt32 GetBigNumberSize(UInt64 value) +{ + int i; + for (i = 0; i < 8; i++) + if (value < ((UInt64(1) << ( 7 * (i + 1))))) + break; + return 1 + i; +} + +UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props) +{ + UInt32 result = GetBigNumberSize(dataSize) * 2 + 41; + if (nameLength != 0) + { + nameLength = (nameLength + 1) * 2; + result += nameLength + GetBigNumberSize(nameLength) + 2; + } + if (props) + { + result += 20; + } + if (result >= 128) + result++; + result += kSignatureSize + 2 + kFinishHeaderSize; + return result; +} + +UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props) +{ + UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props); + int testSize; + if (volSize > headersSizeBase) + testSize = volSize - headersSizeBase; + else + testSize = 1; + UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props); + UInt64 pureSize = 1; + if (volSize > headersSize) + pureSize = volSize - headersSize; + return pureSize; +} +#endif + +HRESULT COutArchive::WriteFolder(const CFolder &folder) +{ + RINOK(WriteNumber(folder.Coders.Size())); + int i; + for (i = 0; i < folder.Coders.Size(); i++) + { + const CCoderInfo &coder = folder.Coders[i]; + { + size_t propertiesSize = coder.Properties.GetCapacity(); + + UInt64 id = coder.MethodID; + int idSize; + for (idSize = 1; idSize < sizeof(id); idSize++) + if ((id >> (8 * idSize)) == 0) + break; + BYTE longID[15]; + for (int t = idSize - 1; t >= 0 ; t--, id >>= 8) + longID[t] = (Byte)(id & 0xFF); + Byte b; + b = (Byte)(idSize & 0xF); + bool isComplex = !coder.IsSimpleCoder(); + b |= (isComplex ? 0x10 : 0); + b |= ((propertiesSize != 0) ? 0x20 : 0 ); + RINOK(WriteByte(b)); + RINOK(WriteBytes(longID, idSize)); + if (isComplex) + { + RINOK(WriteNumber(coder.NumInStreams)); + RINOK(WriteNumber(coder.NumOutStreams)); + } + if (propertiesSize == 0) + continue; + RINOK(WriteNumber(propertiesSize)); + RINOK(WriteBytes(coder.Properties, propertiesSize)); + } + } + for (i = 0; i < folder.BindPairs.Size(); i++) + { + const CBindPair &bindPair = folder.BindPairs[i]; + RINOK(WriteNumber(bindPair.InIndex)); + RINOK(WriteNumber(bindPair.OutIndex)); + } + if (folder.PackStreams.Size() > 1) + for (i = 0; i < folder.PackStreams.Size(); i++) + { + RINOK(WriteNumber(folder.PackStreams[i])); + } + return S_OK; +} + +HRESULT COutArchive::WriteBoolVector(const CBoolVector &boolVector) +{ + Byte b = 0; + Byte mask = 0x80; + for(int i = 0; i < boolVector.Size(); i++) + { + if (boolVector[i]) + b |= mask; + mask >>= 1; + if (mask == 0) + { + RINOK(WriteByte(b)); + mask = 0x80; + b = 0; + } + } + if (mask != 0x80) + { + RINOK(WriteByte(b)); + } + return S_OK; +} + + +HRESULT COutArchive::WriteHashDigests( + const CRecordVector &digestsDefined, + const CRecordVector &digests) +{ + int numDefined = 0; + int i; + for(i = 0; i < digestsDefined.Size(); i++) + if (digestsDefined[i]) + numDefined++; + if (numDefined == 0) + return S_OK; + + RINOK(WriteByte(NID::kCRC)); + if (numDefined == digestsDefined.Size()) + { + RINOK(WriteByte(1)); + } + else + { + RINOK(WriteByte(0)); + RINOK(WriteBoolVector(digestsDefined)); + } + for(i = 0; i < digests.Size(); i++) + { + if(digestsDefined[i]) + RINOK(WriteUInt32(digests[i])); + } + return S_OK; +} + +HRESULT COutArchive::WritePackInfo( + UInt64 dataOffset, + const CRecordVector &packSizes, + const CRecordVector &packCRCsDefined, + const CRecordVector &packCRCs) +{ + if (packSizes.IsEmpty()) + return S_OK; + RINOK(WriteByte(NID::kPackInfo)); + RINOK(WriteNumber(dataOffset)); + RINOK(WriteNumber(packSizes.Size())); + RINOK(WriteByte(NID::kSize)); + for(int i = 0; i < packSizes.Size(); i++) + RINOK(WriteNumber(packSizes[i])); + + RINOK(WriteHashDigests(packCRCsDefined, packCRCs)); + + return WriteByte(NID::kEnd); +} + +HRESULT COutArchive::WriteUnPackInfo(const CObjectVector &folders) +{ + if (folders.IsEmpty()) + return S_OK; + + RINOK(WriteByte(NID::kUnPackInfo)); + + RINOK(WriteByte(NID::kFolder)); + RINOK(WriteNumber(folders.Size())); + { + RINOK(WriteByte(0)); + for(int i = 0; i < folders.Size(); i++) + RINOK(WriteFolder(folders[i])); + } + + RINOK(WriteByte(NID::kCodersUnPackSize)); + int i; + for(i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + for (int j = 0; j < folder.UnPackSizes.Size(); j++) + RINOK(WriteNumber(folder.UnPackSizes[j])); + } + + CRecordVector unPackCRCsDefined; + CRecordVector unPackCRCs; + for(i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + unPackCRCsDefined.Add(folder.UnPackCRCDefined); + unPackCRCs.Add(folder.UnPackCRC); + } + RINOK(WriteHashDigests(unPackCRCsDefined, unPackCRCs)); + + return WriteByte(NID::kEnd); +} + +HRESULT COutArchive::WriteSubStreamsInfo( + const CObjectVector &folders, + const CRecordVector &numUnPackStreamsInFolders, + const CRecordVector &unPackSizes, + const CRecordVector &digestsDefined, + const CRecordVector &digests) +{ + RINOK(WriteByte(NID::kSubStreamsInfo)); + + int i; + for(i = 0; i < numUnPackStreamsInFolders.Size(); i++) + { + if (numUnPackStreamsInFolders[i] != 1) + { + RINOK(WriteByte(NID::kNumUnPackStream)); + for(i = 0; i < numUnPackStreamsInFolders.Size(); i++) + RINOK(WriteNumber(numUnPackStreamsInFolders[i])); + break; + } + } + + + bool needFlag = true; + CNum index = 0; + for(i = 0; i < numUnPackStreamsInFolders.Size(); i++) + for (CNum j = 0; j < numUnPackStreamsInFolders[i]; j++) + { + if (j + 1 != numUnPackStreamsInFolders[i]) + { + if (needFlag) + RINOK(WriteByte(NID::kSize)); + needFlag = false; + RINOK(WriteNumber(unPackSizes[index])); + } + index++; + } + + CRecordVector digestsDefined2; + CRecordVector digests2; + + int digestIndex = 0; + for (i = 0; i < folders.Size(); i++) + { + int numSubStreams = (int)numUnPackStreamsInFolders[i]; + if (numSubStreams == 1 && folders[i].UnPackCRCDefined) + digestIndex++; + else + for (int j = 0; j < numSubStreams; j++, digestIndex++) + { + digestsDefined2.Add(digestsDefined[digestIndex]); + digests2.Add(digests[digestIndex]); + } + } + RINOK(WriteHashDigests(digestsDefined2, digests2)); + return WriteByte(NID::kEnd); +} + +HRESULT COutArchive::WriteTime( + const CObjectVector &files, Byte type) +{ + ///////////////////////////////////////////////// + // CreationTime + CBoolVector boolVector; + boolVector.Reserve(files.Size()); + bool thereAreDefined = false; + bool allDefined = true; + int i; + for(i = 0; i < files.Size(); i++) + { + const CFileItem &item = files[i]; + bool defined; + switch(type) + { + case NID::kCreationTime: + defined = item.IsCreationTimeDefined; + break; + case NID::kLastWriteTime: + defined = item.IsLastWriteTimeDefined; + break; + case NID::kLastAccessTime: + defined = item.IsLastAccessTimeDefined; + break; + default: + throw 1; + } + boolVector.Add(defined); + thereAreDefined = (thereAreDefined || defined); + allDefined = (allDefined && defined); + } + if (!thereAreDefined) + return S_OK; + RINOK(WriteByte(type)); + size_t dataSize = 1 + 1; + dataSize += files.Size() * 8; + if (allDefined) + { + RINOK(WriteNumber(dataSize)); + WriteByte(1); + } + else + { + RINOK(WriteNumber(1 + (boolVector.Size() + 7) / 8 + dataSize)); + WriteByte(0); + RINOK(WriteBoolVector(boolVector)); + } + RINOK(WriteByte(0)); + for(i = 0; i < files.Size(); i++) + { + if (boolVector[i]) + { + const CFileItem &item = files[i]; + CArchiveFileTime timeValue; + timeValue.dwLowDateTime = 0; + timeValue.dwHighDateTime = 0; + switch(type) + { + case NID::kCreationTime: + timeValue = item.CreationTime; + break; + case NID::kLastWriteTime: + timeValue = item.LastWriteTime; + break; + case NID::kLastAccessTime: + timeValue = item.LastAccessTime; + break; + } + RINOK(WriteUInt32(timeValue.dwLowDateTime)); + RINOK(WriteUInt32(timeValue.dwHighDateTime)); + } + } + return S_OK; +} + +HRESULT COutArchive::EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const Byte *data, size_t dataSize, + CRecordVector &packSizes, CObjectVector &folders) +{ + CSequentialInStreamImp *streamSpec = new CSequentialInStreamImp; + CMyComPtr stream = streamSpec; + streamSpec->Init(data, dataSize); + CFolder folderItem; + folderItem.UnPackCRCDefined = true; + folderItem.UnPackCRC = CrcCalc(data, dataSize); + UInt64 dataSize64 = dataSize; + RINOK(encoder.Encode( + EXTERNAL_CODECS_LOC_VARS + stream, NULL, &dataSize64, folderItem, SeqStream, packSizes, NULL)) + folders.Add(folderItem); + return S_OK; +} + +HRESULT COutArchive::EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const CByteBuffer &data, + CRecordVector &packSizes, CObjectVector &folders) +{ + return EncodeStream( + EXTERNAL_CODECS_LOC_VARS + encoder, data, data.GetCapacity(), packSizes, folders); +} + +static void WriteUInt32ToBuffer(Byte *data, UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + *data++ = (Byte)value; + value >>= 8; + } +} + +static void WriteUInt64ToBuffer(Byte *data, UInt64 value) +{ + for (int i = 0; i < 8; i++) + { + *data++ = (Byte)value; + value >>= 8; + } +} + + +HRESULT COutArchive::WriteHeader( + const CArchiveDatabase &database, + const CHeaderOptions &headerOptions, + UInt64 &headerOffset) +{ + int i; + + ///////////////////////////////// + // Names + + CNum numDefinedNames = 0; + size_t namesDataSize = 0; + for(i = 0; i < database.Files.Size(); i++) + { + const UString &name = database.Files[i].Name; + if (!name.IsEmpty()) + numDefinedNames++; + namesDataSize += (name.Length() + 1) * 2; + } + + CByteBuffer namesData; + if (numDefinedNames > 0) + { + namesData.SetCapacity((size_t)namesDataSize); + size_t pos = 0; + for(int i = 0; i < database.Files.Size(); i++) + { + const UString &name = database.Files[i].Name; + for (int t = 0; t < name.Length(); t++) + { + wchar_t c = name[t]; + namesData[pos++] = Byte(c); + namesData[pos++] = Byte(c >> 8); + } + namesData[pos++] = 0; + namesData[pos++] = 0; + } + } + + ///////////////////////////////// + // Write Attributes + CBoolVector attributesBoolVector; + attributesBoolVector.Reserve(database.Files.Size()); + int numDefinedAttributes = 0; + for(i = 0; i < database.Files.Size(); i++) + { + bool defined = database.Files[i].AreAttributesDefined; + attributesBoolVector.Add(defined); + if (defined) + numDefinedAttributes++; + } + + CByteBuffer attributesData; + if (numDefinedAttributes > 0) + { + attributesData.SetCapacity(numDefinedAttributes * 4); + size_t pos = 0; + for(i = 0; i < database.Files.Size(); i++) + { + const CFileItem &file = database.Files[i]; + if (file.AreAttributesDefined) + { + WriteUInt32ToBuffer(attributesData + pos, file.Attributes); + pos += 4; + } + } + } + + ///////////////////////////////// + // Write StartPos + CBoolVector startsBoolVector; + startsBoolVector.Reserve(database.Files.Size()); + int numDefinedStarts = 0; + for(i = 0; i < database.Files.Size(); i++) + { + bool defined = database.Files[i].IsStartPosDefined; + startsBoolVector.Add(defined); + if (defined) + numDefinedStarts++; + } + + CByteBuffer startsData; + if (numDefinedStarts > 0) + { + startsData.SetCapacity(numDefinedStarts * 8); + size_t pos = 0; + for(i = 0; i < database.Files.Size(); i++) + { + const CFileItem &file = database.Files[i]; + if (file.IsStartPosDefined) + { + WriteUInt64ToBuffer(startsData + pos, file.StartPos); + pos += 8; + } + } + } + + ///////////////////////////////// + // Write Last Write Time + // /* + CNum numDefinedLastWriteTimes = 0; + for(i = 0; i < database.Files.Size(); i++) + if (database.Files[i].IsLastWriteTimeDefined) + numDefinedLastWriteTimes++; + + if (numDefinedLastWriteTimes > 0) + { + CByteBuffer lastWriteTimeData; + lastWriteTimeData.SetCapacity(numDefinedLastWriteTimes * 8); + size_t pos = 0; + for(i = 0; i < database.Files.Size(); i++) + { + const CFileItem &file = database.Files[i]; + if (file.IsLastWriteTimeDefined) + { + WriteUInt32ToBuffer(lastWriteTimeData + pos, file.LastWriteTime.dwLowDateTime); + pos += 4; + WriteUInt32ToBuffer(lastWriteTimeData + pos, file.LastWriteTime.dwHighDateTime); + pos += 4; + } + } + } + // */ + + + UInt64 packedSize = 0; + for(i = 0; i < database.PackSizes.Size(); i++) + packedSize += database.PackSizes[i]; + + headerOffset = packedSize; + + _mainMode = true; + + _outByte.SetStream(SeqStream); + _outByte.Init(); + _crc = CRC_INIT_VAL; + + + RINOK(WriteByte(NID::kHeader)); + + // Archive Properties + + if (database.Folders.Size() > 0) + { + RINOK(WriteByte(NID::kMainStreamsInfo)); + RINOK(WritePackInfo(0, database.PackSizes, + database.PackCRCsDefined, + database.PackCRCs)); + + RINOK(WriteUnPackInfo(database.Folders)); + + CRecordVector unPackSizes; + CRecordVector digestsDefined; + CRecordVector digests; + for (i = 0; i < database.Files.Size(); i++) + { + const CFileItem &file = database.Files[i]; + if (!file.HasStream) + continue; + unPackSizes.Add(file.UnPackSize); + digestsDefined.Add(file.IsFileCRCDefined); + digests.Add(file.FileCRC); + } + + RINOK(WriteSubStreamsInfo( + database.Folders, + database.NumUnPackStreamsVector, + unPackSizes, + digestsDefined, + digests)); + RINOK(WriteByte(NID::kEnd)); + } + + if (database.Files.IsEmpty()) + { + RINOK(WriteByte(NID::kEnd)); + return _outByte.Flush(); + } + + RINOK(WriteByte(NID::kFilesInfo)); + RINOK(WriteNumber(database.Files.Size())); + + CBoolVector emptyStreamVector; + emptyStreamVector.Reserve(database.Files.Size()); + int numEmptyStreams = 0; + for(i = 0; i < database.Files.Size(); i++) + if (database.Files[i].HasStream) + emptyStreamVector.Add(false); + else + { + emptyStreamVector.Add(true); + numEmptyStreams++; + } + if (numEmptyStreams > 0) + { + RINOK(WriteByte(NID::kEmptyStream)); + RINOK(WriteNumber((emptyStreamVector.Size() + 7) / 8)); + RINOK(WriteBoolVector(emptyStreamVector)); + + CBoolVector emptyFileVector, antiVector; + emptyFileVector.Reserve(numEmptyStreams); + antiVector.Reserve(numEmptyStreams); + CNum numEmptyFiles = 0, numAntiItems = 0; + for(i = 0; i < database.Files.Size(); i++) + { + const CFileItem &file = database.Files[i]; + if (!file.HasStream) + { + emptyFileVector.Add(!file.IsDirectory); + if (!file.IsDirectory) + numEmptyFiles++; + antiVector.Add(file.IsAnti); + if (file.IsAnti) + numAntiItems++; + } + } + + if (numEmptyFiles > 0) + { + RINOK(WriteByte(NID::kEmptyFile)); + RINOK(WriteNumber((emptyFileVector.Size() + 7) / 8)); + RINOK(WriteBoolVector(emptyFileVector)); + } + + if (numAntiItems > 0) + { + RINOK(WriteByte(NID::kAnti)); + RINOK(WriteNumber((antiVector.Size() + 7) / 8)); + RINOK(WriteBoolVector(antiVector)); + } + } + + if (numDefinedNames > 0) + { + ///////////////////////////////////////////////// + RINOK(WriteByte(NID::kName)); + { + RINOK(WriteNumber(1 + namesData.GetCapacity())); + RINOK(WriteByte(0)); + RINOK(WriteBytes(namesData)); + } + + } + + if (headerOptions.WriteCreated) + { + RINOK(WriteTime(database.Files, NID::kCreationTime)); + } + if (headerOptions.WriteModified) + { + RINOK(WriteTime(database.Files, NID::kLastWriteTime)); + } + if (headerOptions.WriteAccessed) + { + RINOK(WriteTime(database.Files, NID::kLastAccessTime)); + } + + if (numDefinedAttributes > 0) + { + RINOK(WriteByte(NID::kWinAttributes)); + size_t size = 2; + if (numDefinedAttributes != database.Files.Size()) + size += (attributesBoolVector.Size() + 7) / 8 + 1; + size += attributesData.GetCapacity(); + + RINOK(WriteNumber(size)); + if (numDefinedAttributes == database.Files.Size()) + { + RINOK(WriteByte(1)); + } + else + { + RINOK(WriteByte(0)); + RINOK(WriteBoolVector(attributesBoolVector)); + } + + { + RINOK(WriteByte(0)); + RINOK(WriteBytes(attributesData)); + } + } + + if (numDefinedStarts > 0) + { + RINOK(WriteByte(NID::kStartPos)); + size_t size = 2; + if (numDefinedStarts != database.Files.Size()) + size += (startsBoolVector.Size() + 7) / 8 + 1; + size += startsData.GetCapacity(); + + RINOK(WriteNumber(size)); + if (numDefinedStarts == database.Files.Size()) + { + RINOK(WriteByte(1)); + } + else + { + RINOK(WriteByte(0)); + RINOK(WriteBoolVector(startsBoolVector)); + } + + { + RINOK(WriteByte(0)); + RINOK(WriteBytes(startsData)); + } + } + + RINOK(WriteByte(NID::kEnd)); // for files + RINOK(WriteByte(NID::kEnd)); // for headers + + return _outByte.Flush(); +} + +HRESULT COutArchive::WriteDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + const CArchiveDatabase &database, + const CCompressionMethodMode *options, + const CHeaderOptions &headerOptions) +{ + UInt64 headerOffset; + UInt32 headerCRC; + UInt64 headerSize; + if (database.IsEmpty()) + { + headerSize = 0; + headerOffset = 0; + headerCRC = CrcCalc(0, 0); + } + else + { + _dynamicBuffer.Init(); + _dynamicMode = false; + + if (options != 0) + if (options->IsEmpty()) + options = 0; + if (options != 0) + if (options->PasswordIsDefined || headerOptions.CompressMainHeader) + _dynamicMode = true; + RINOK(WriteHeader(database, headerOptions, headerOffset)); + + if (_dynamicMode) + { + CCompressionMethodMode encryptOptions; + encryptOptions.PasswordIsDefined = options->PasswordIsDefined; + encryptOptions.Password = options->Password; + CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions); + CRecordVector packSizes; + CObjectVector folders; + RINOK(EncodeStream( + EXTERNAL_CODECS_LOC_VARS + encoder, _dynamicBuffer, + _dynamicBuffer.GetSize(), packSizes, folders)); + _dynamicMode = false; + _mainMode = true; + + _outByte.SetStream(SeqStream); + _outByte.Init(); + _crc = CRC_INIT_VAL; + + if (folders.Size() == 0) + throw 1; + + RINOK(WriteID(NID::kEncodedHeader)); + RINOK(WritePackInfo(headerOffset, packSizes, + CRecordVector(), CRecordVector())); + RINOK(WriteUnPackInfo(folders)); + RINOK(WriteByte(NID::kEnd)); + for (int i = 0; i < packSizes.Size(); i++) + headerOffset += packSizes[i]; + RINOK(_outByte.Flush()); + } + headerCRC = CRC_GET_DIGEST(_crc); + headerSize = _outByte.GetProcessedSize(); + } + #ifdef _7Z_VOL + if (_endMarker) + { + CFinishHeader h; + h.NextHeaderSize = headerSize; + h.NextHeaderCRC = headerCRC; + h.NextHeaderOffset = + UInt64(0) - (headerSize + + 4 + kFinishHeaderSize); + h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset; + h.AdditionalStartBlockSize = 0; + RINOK(WriteFinishHeader(h)); + return WriteFinishSignature(); + } + else + #endif + { + CStartHeader h; + h.NextHeaderSize = headerSize; + h.NextHeaderCRC = headerCRC; + h.NextHeaderOffset = headerOffset; + RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL)); + return WriteStartHeader(h); + } +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.h new file mode 100644 index 0000000..fd79818 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zOut.h @@ -0,0 +1,193 @@ +// 7z/Out.h + +#ifndef __7Z_OUT_H +#define __7Z_OUT_H + +#include "7zHeader.h" +#include "7zItem.h" +#include "7zCompressionMode.h" +#include "7zEncode.h" + +#include "../../Common/OutBuffer.h" +#include "../../../Common/DynamicBuffer.h" + +namespace NArchive { +namespace N7z { + +class CWriteBufferLoc +{ + Byte *_data; + size_t _size; + size_t _pos; +public: + CWriteBufferLoc(): _size(0), _pos(0) {} + void Init(Byte *data, size_t size) + { + _pos = 0; + _data = data; + _size = size; + } + HRESULT Write(const void *data, size_t size) + { + if (_pos + size > _size) + return E_FAIL; + memmove(_data + _pos, data, size); + _pos += size; + return S_OK; + } +}; + +class CWriteDynamicBuffer +{ + CByteDynamicBuffer _buffer; + size_t _pos; +public: + CWriteDynamicBuffer(): _pos(0) {} + void Init() + { + _pos = 0; + } + void Write(const void *data, size_t size) + { + if (_pos + size > _buffer.GetCapacity()) + _buffer.EnsureCapacity(_pos + size); + memmove(((Byte *)_buffer) +_pos, data, size); + _pos += size; + } + operator Byte *() { return (Byte *)_buffer; }; + operator const Byte *() const { return (const Byte *)_buffer; }; + size_t GetSize() const { return _pos; } +}; + +struct CHeaderOptions +{ + // bool UseAdditionalHeaderStreams; + bool CompressMainHeader; + bool WriteModified; + bool WriteCreated; + bool WriteAccessed; + + CHeaderOptions(): + // UseAdditionalHeaderStreams(false), + CompressMainHeader(true), + WriteModified(true), + WriteCreated(false), + WriteAccessed(false) {} +}; + +class COutArchive +{ + UInt64 _prefixHeaderPos; + + HRESULT WriteDirect(const void *data, UInt32 size); + HRESULT WriteDirectByte(Byte b) { return WriteDirect(&b, 1); } + HRESULT WriteDirectUInt32(UInt32 value); + HRESULT WriteDirectUInt64(UInt64 value); + + HRESULT WriteBytes(const void *data, size_t size); + HRESULT WriteBytes(const CByteBuffer &data); + HRESULT WriteByte(Byte b); + HRESULT WriteUInt32(UInt32 value); + HRESULT WriteNumber(UInt64 value); + HRESULT WriteID(UInt64 value) { return WriteNumber(value); } + + HRESULT WriteFolder(const CFolder &folder); + HRESULT WriteFileHeader(const CFileItem &itemInfo); + HRESULT WriteBoolVector(const CBoolVector &boolVector); + HRESULT WriteHashDigests( + const CRecordVector &digestsDefined, + const CRecordVector &hashDigests); + + HRESULT WritePackInfo( + UInt64 dataOffset, + const CRecordVector &packSizes, + const CRecordVector &packCRCsDefined, + const CRecordVector &packCRCs); + + HRESULT WriteUnPackInfo(const CObjectVector &folders); + + HRESULT WriteSubStreamsInfo( + const CObjectVector &folders, + const CRecordVector &numUnPackStreamsInFolders, + const CRecordVector &unPackSizes, + const CRecordVector &digestsDefined, + const CRecordVector &hashDigests); + + /* + HRESULT WriteStreamsInfo( + UInt64 dataOffset, + const CRecordVector &packSizes, + const CRecordVector &packCRCsDefined, + const CRecordVector &packCRCs, + bool externalFolders, + UInt64 externalFoldersStreamIndex, + const CObjectVector &folders, + const CRecordVector &numUnPackStreamsInFolders, + const CRecordVector &unPackSizes, + const CRecordVector &digestsDefined, + const CRecordVector &hashDigests); + */ + + + HRESULT WriteTime(const CObjectVector &files, Byte type); + + HRESULT EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const Byte *data, size_t dataSize, + CRecordVector &packSizes, CObjectVector &folders); + HRESULT EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const CByteBuffer &data, + CRecordVector &packSizes, CObjectVector &folders); + HRESULT WriteHeader( + const CArchiveDatabase &database, + const CHeaderOptions &headerOptions, + UInt64 &headerOffset); + + bool _mainMode; + + bool _dynamicMode; + + bool _countMode; + size_t _countSize; + COutBuffer _outByte; + CWriteBufferLoc _outByte2; + CWriteDynamicBuffer _dynamicBuffer; + UInt32 _crc; + + #ifdef _7Z_VOL + bool _endMarker; + #endif + + HRESULT WriteSignature(); + #ifdef _7Z_VOL + HRESULT WriteFinishSignature(); + #endif + HRESULT WriteStartHeader(const CStartHeader &h); + #ifdef _7Z_VOL + HRESULT WriteFinishHeader(const CFinishHeader &h); + #endif + CMyComPtr Stream; +public: + + COutArchive() { _outByte.Create(1 << 16); } + CMyComPtr SeqStream; + HRESULT Create(ISequentialOutStream *stream, bool endMarker); + void Close(); + HRESULT SkeepPrefixArchiveHeader(); + HRESULT WriteDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + const CArchiveDatabase &database, + const CCompressionMethodMode *options, + const CHeaderOptions &headerOptions); + + #ifdef _7Z_VOL + static UInt32 GetVolHeadersSize(UInt64 dataSize, int nameLength = 0, bool props = false); + static UInt64 GetVolPureSize(UInt64 volSize, int nameLength = 0, bool props = false); + #endif + +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.cpp new file mode 100644 index 0000000..3452a03 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.cpp @@ -0,0 +1,162 @@ +// 7zProperties.cpp + +#include "StdAfx.h" + +#include "7zProperties.h" +#include "7zHeader.h" +#include "7zHandler.h" + +// #define _MULTI_PACK + +namespace NArchive { +namespace N7z { + +struct CPropMap +{ + UInt64 FilePropID; + STATPROPSTG StatPROPSTG; +}; + +CPropMap kPropMap[] = +{ + { NID::kName, NULL, kpidPath, VT_BSTR}, + { NID::kSize, NULL, kpidSize, VT_UI8}, + { NID::kPackInfo, NULL, kpidPackedSize, VT_UI8}, + + #ifdef _MULTI_PACK + { 100, L"Pack0", kpidPackedSize0, VT_UI8}, + { 101, L"Pack1", kpidPackedSize1, VT_UI8}, + { 102, L"Pack2", kpidPackedSize2, VT_UI8}, + { 103, L"Pack3", kpidPackedSize3, VT_UI8}, + { 104, L"Pack4", kpidPackedSize4, VT_UI8}, + #endif + + { NID::kCreationTime, NULL, kpidCreationTime, VT_FILETIME}, + { NID::kLastWriteTime, NULL, kpidLastWriteTime, VT_FILETIME}, + { NID::kLastAccessTime, NULL, kpidLastAccessTime, VT_FILETIME}, + { NID::kWinAttributes, NULL, kpidAttributes, VT_UI4}, + { NID::kStartPos, NULL, kpidPosition, VT_UI4}, + + { NID::kCRC, NULL, kpidCRC, VT_UI4}, + + { NID::kAnti, NULL, kpidIsAnti, VT_BOOL}, + // { 97, NULL, kpidSolid, VT_BOOL}, + #ifndef _SFX + { 98, NULL, kpidMethod, VT_BSTR}, + { 99, NULL, kpidBlock, VT_UI4} + #endif +}; + +static const int kPropMapSize = sizeof(kPropMap) / sizeof(kPropMap[0]); + +static int FindPropInMap(UInt64 filePropID) +{ + for (int i = 0; i < kPropMapSize; i++) + if (kPropMap[i].FilePropID == filePropID) + return i; + return -1; +} + +static void CopyOneItem(CRecordVector &src, + CRecordVector &dest, UInt32 item) +{ + for (int i = 0; i < src.Size(); i++) + if (src[i] == item) + { + dest.Add(item); + src.Delete(i); + return; + } +} + +static void RemoveOneItem(CRecordVector &src, UInt32 item) +{ + for (int i = 0; i < src.Size(); i++) + if (src[i] == item) + { + src.Delete(i); + return; + } +} + +static void InsertToHead(CRecordVector &dest, UInt32 item) +{ + for (int i = 0; i < dest.Size(); i++) + if (dest[i] == item) + { + dest.Delete(i); + break; + } + dest.Insert(0, item); +} + +void CHandler::FillPopIDs() +{ + _fileInfoPopIDs.Clear(); + + #ifdef _7Z_VOL + if(_volumes.Size() < 1) + return; + const CVolume &volume = _volumes.Front(); + const CArchiveDatabaseEx &_database = volume.Database; + #endif + + CRecordVector fileInfoPopIDs = _database.ArchiveInfo.FileInfoPopIDs; + + RemoveOneItem(fileInfoPopIDs, NID::kEmptyStream); + RemoveOneItem(fileInfoPopIDs, NID::kEmptyFile); + + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kName); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kAnti); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kSize); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kPackInfo); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCreationTime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kLastWriteTime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kLastAccessTime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kWinAttributes); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCRC); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kComment); + _fileInfoPopIDs += fileInfoPopIDs; + + #ifndef _SFX + _fileInfoPopIDs.Add(98); + _fileInfoPopIDs.Add(99); + #endif + #ifdef _MULTI_PACK + _fileInfoPopIDs.Add(100); + _fileInfoPopIDs.Add(101); + _fileInfoPopIDs.Add(102); + _fileInfoPopIDs.Add(103); + _fileInfoPopIDs.Add(104); + #endif + + #ifndef _SFX + InsertToHead(_fileInfoPopIDs, NID::kLastWriteTime); + InsertToHead(_fileInfoPopIDs, NID::kPackInfo); + InsertToHead(_fileInfoPopIDs, NID::kSize); + InsertToHead(_fileInfoPopIDs, NID::kName); + #endif +} + +STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) +{ + *numProperties = _fileInfoPopIDs.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, + BSTR *name, PROPID *propID, VARTYPE *varType) +{ + if((int)index >= _fileInfoPopIDs.Size()) + return E_INVALIDARG; + int indexInMap = FindPropInMap(_fileInfoPopIDs[index]); + if (indexInMap == -1) + return E_INVALIDARG; + const STATPROPSTG &srcItem = kPropMap[indexInMap].StatPROPSTG; + *propID = srcItem.propid; + *varType = srcItem.vt; + *name = 0; + return S_OK; +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.h new file mode 100644 index 0000000..4da85f0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zProperties.h @@ -0,0 +1,22 @@ +// 7zProperties.h + +#ifndef __7Z_PROPERTIES_H +#define __7Z_PROPERTIES_H + +#include "../../PropID.h" + +namespace NArchive { +namespace N7z { + +enum +{ + kpidPackedSize0 = kpidUserDefined, + kpidPackedSize1, + kpidPackedSize2, + kpidPackedSize3, + kpidPackedSize4 +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zRegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zRegister.cpp new file mode 100644 index 0000000..e18c4d7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zRegister.cpp @@ -0,0 +1,18 @@ +// 7zRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "7zHandler.h" +static IInArchive *CreateArc() { return new NArchive::N7z::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::N7z::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"7z", L"7z", 0, 7, {'7' + 1 , 'z', 0xBC, 0xAF, 0x27, 0x1C}, 6, false, CreateArc, CreateArcOut }; + +REGISTER_ARC_DEC_SIG(7z) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp new file mode 100644 index 0000000..80d303a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp @@ -0,0 +1,24 @@ +// 7zSpecStream.cpp + +#include "StdAfx.h" + +#include "7zSpecStream.h" + +STDMETHODIMP CSequentialInStreamSizeCount2::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + if (processedSize != 0) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CSequentialInStreamSizeCount2::GetSubStreamSize( + UInt64 subStream, UInt64 *value) +{ + if (_getSubStreamSize == NULL) + return E_NOTIMPL; + return _getSubStreamSize->GetSubStreamSize(subStream, value); +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.h new file mode 100644 index 0000000..0253c42 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.h @@ -0,0 +1,35 @@ +// 7zSpecStream.h + +#ifndef __7Z_SPEC_STREAM_H +#define __7Z_SPEC_STREAM_H + +#include "../../IStream.h" +#include "../../ICoder.h" +#include "../../../Common/MyCom.h" + +class CSequentialInStreamSizeCount2: + public ISequentialInStream, + public ICompressGetSubStreamSize, + public CMyUnknownImp +{ + CMyComPtr _stream; + CMyComPtr _getSubStreamSize; + UInt64 _size; +public: + void Init(ISequentialInStream *stream) + { + _stream = stream; + _getSubStreamSize = 0; + _stream.QueryInterface(IID_ICompressGetSubStreamSize, &_getSubStreamSize); + _size = 0; + } + UInt64 GetSize() const { return _size; } + + MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp new file mode 100644 index 0000000..68f4604 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -0,0 +1,1029 @@ +// UpdateMain.cpp + +#include "StdAfx.h" + +#include "7zUpdate.h" +#include "7zFolderInStream.h" +#include "7zEncode.h" +#include "7zHandler.h" +#include "7zOut.h" + +#include "../../Compress/Copy/CopyCoder.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/LimitedStreams.h" +#include "../Common/ItemNameUtils.h" + +namespace NArchive { +namespace N7z { + +static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2"; +static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20; +static const UInt32 kAlgorithmForBCJ2_LZMA = 1; +static const UInt32 kNumFastBytesForBCJ2_LZMA = 64; + +static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream, + UInt64 position, UInt64 size, ICompressProgressInfo *progress) +{ + RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0)); + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr inStreamLimited(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(size); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr copyCoder = copyCoderSpec; + RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); + return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL); +} + +static int GetReverseSlashPos(const UString &name) +{ + int slashPos = name.ReverseFind(L'/'); + #ifdef _WIN32 + int slash1Pos = name.ReverseFind(L'\\'); + slashPos = MyMax(slashPos, slash1Pos); + #endif + return slashPos; +} + +int CUpdateItem::GetExtensionPos() const +{ + int slashPos = GetReverseSlashPos(Name); + int dotPos = Name.ReverseFind(L'.'); + if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) + return Name.Length(); + return dotPos + 1; +} + +UString CUpdateItem::GetExtension() const +{ + return Name.Mid(GetExtensionPos()); +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2) +{ + size_t c1 = a1.GetCapacity(); + size_t c2 = a2.GetCapacity(); + RINOZ(MyCompare(c1, c2)); + for (size_t i = 0; i < c1; i++) + RINOZ(MyCompare(a1[i], a2[i])); + return 0; +} + +static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2) +{ + RINOZ(MyCompare(c1.NumInStreams, c2.NumInStreams)); + RINOZ(MyCompare(c1.NumOutStreams, c2.NumOutStreams)); + RINOZ(MyCompare(c1.MethodID, c2.MethodID)); + return CompareBuffers(c1.Properties, c2.Properties); +} + +static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2) +{ + RINOZ(MyCompare(b1.InIndex, b2.InIndex)); + return MyCompare(b1.OutIndex, b2.OutIndex); +} + +static int CompareFolders(const CFolder &f1, const CFolder &f2) +{ + int s1 = f1.Coders.Size(); + int s2 = f2.Coders.Size(); + RINOZ(MyCompare(s1, s2)); + int i; + for (i = 0; i < s1; i++) + RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i])); + s1 = f1.BindPairs.Size(); + s2 = f2.BindPairs.Size(); + RINOZ(MyCompare(s1, s2)); + for (i = 0; i < s1; i++) + RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i])); + return 0; +} + +static int CompareFiles(const CFileItem &f1, const CFileItem &f2) +{ + return MyStringCompareNoCase(f1.Name, f2.Name); +} + +static int CompareFolderRefs(const int *p1, const int *p2, void *param) +{ + int i1 = *p1; + int i2 = *p2; + const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param; + RINOZ(CompareFolders( + db.Folders[i1], + db.Folders[i2])); + RINOZ(MyCompare( + db.NumUnPackStreamsVector[i1], + db.NumUnPackStreamsVector[i2])); + if (db.NumUnPackStreamsVector[i1] == 0) + return 0; + return CompareFiles( + db.Files[db.FolderStartFileIndex[i1]], + db.Files[db.FolderStartFileIndex[i2]]); +} + +//////////////////////////////////////////////////////////// + +static int CompareEmptyItems(const int *p1, const int *p2, void *param) +{ + const CObjectVector &updateItems = *(const CObjectVector *)param; + const CUpdateItem &u1 = updateItems[*p1]; + const CUpdateItem &u2 = updateItems[*p2]; + if (u1.IsDirectory != u2.IsDirectory) + return (u1.IsDirectory) ? 1 : -1; + if (u1.IsDirectory) + { + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + int n = MyStringCompareNoCase(u1.Name, u2.Name); + return -n; + } + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + return MyStringCompareNoCase(u1.Name, u2.Name); +} + +static const char *g_Exts = + " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo" + " zip jar ear war msi" + " 3gp avi mov mpeg mpg mpe wmv" + " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav" + " swf " + " chm hxi hxs" + " gif jpeg jpg jp2 png tiff bmp ico psd psp" + " awg ps eps cgm dxf svg vrml wmf emf ai md" + " cad dwg pps key sxi" + " max 3ds" + " iso bin nrg mdf img pdi tar cpio xpi" + " vfd vhd vud vmc vsv" + " vmdk dsk nvram vmem vmsd vmsn vmss vmtm" + " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def" + " f77 f f90 f95" + " asm sql manifest dep " + " mak clw csproj vcproj sln dsp dsw " + " class " + " bat cmd" + " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml" + " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs" + " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf" + " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf" + " abw afp cwk lwp wpd wps wpt wrf wri" + " abf afm bdf fon mgf otf pcf pfa snf ttf" + " dbf mdb nsf ntf wdb db fdb gdb" + " exe dll ocx vbx sfx sys tlb awx com obj lib out o so " + " pdb pch idb ncb opt"; + +int GetExtIndex(const char *ext) +{ + int extIndex = 1; + const char *p = g_Exts; + for (;;) + { + char c = *p++; + if (c == 0) + return extIndex; + if (c == ' ') + continue; + int pos = 0; + for (;;) + { + char c2 = ext[pos++]; + if (c2 == 0 && (c == 0 || c == ' ')) + return extIndex; + if (c != c2) + break; + c = *p++; + } + extIndex++; + for (;;) + { + if (c == 0) + return extIndex; + if (c == ' ') + break; + c = *p++; + } + } +} + +struct CRefItem +{ + UInt32 Index; + const CUpdateItem *UpdateItem; + UInt32 ExtensionPos; + UInt32 NamePos; + int ExtensionIndex; + CRefItem(UInt32 index, const CUpdateItem &updateItem, bool sortByType): + Index(index), + UpdateItem(&updateItem), + ExtensionPos(0), + NamePos(0), + ExtensionIndex(0) + { + if (sortByType) + { + int slashPos = GetReverseSlashPos(updateItem.Name); + NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0); + int dotPos = updateItem.Name.ReverseFind(L'.'); + if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) + ExtensionPos = updateItem.Name.Length(); + else + { + ExtensionPos = dotPos + 1; + UString us = updateItem.Name.Mid(ExtensionPos); + if (!us.IsEmpty()) + { + us.MakeLower(); + int i; + AString s; + for (i = 0; i < us.Length(); i++) + { + wchar_t c = us[i]; + if (c >= 0x80) + break; + s += (char)c; + } + if (i == us.Length()) + ExtensionIndex = GetExtIndex(s); + else + ExtensionIndex = 0; + } + } + } + } +}; + +static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param) +{ + const CRefItem &a1 = *p1; + const CRefItem &a2 = *p2; + const CUpdateItem &u1 = *a1.UpdateItem; + const CUpdateItem &u2 = *a2.UpdateItem; + int n; + if (u1.IsDirectory != u2.IsDirectory) + return (u1.IsDirectory) ? 1 : -1; + if (u1.IsDirectory) + { + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + n = MyStringCompareNoCase(u1.Name, u2.Name); + return -n; + } + bool sortByType = *(bool *)param; + if (sortByType) + { + RINOZ(MyCompare(a1.ExtensionIndex, a2.ExtensionIndex)) + RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos)); + RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos)); + if (u1.IsLastWriteTimeDefined && u2.IsLastWriteTimeDefined) + RINOZ(CompareFileTime(&u1.LastWriteTime, &u2.LastWriteTime)); + RINOZ(MyCompare(u1.Size, u2.Size)) + } + return MyStringCompareNoCase(u1.Name, u2.Name); +} + +struct CSolidGroup +{ + CCompressionMethodMode Method; + CRecordVector Indices; +}; + +static wchar_t *g_ExeExts[] = +{ + L"dll", + L"exe", + L"ocx", + L"sfx", + L"sys" +}; + +static bool IsExeFile(const UString &ext) +{ + for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++) + if (ext.CompareNoCase(g_ExeExts[i]) == 0) + return true; + return false; +} + +static const UInt64 k_LZMA = 0x030101; +static const UInt64 k_BCJ = 0x03030103; +static const UInt64 k_BCJ2 = 0x0303011B; + +static bool GetMethodFull(UInt64 methodID, + UInt32 numInStreams, CMethodFull &methodResult) +{ + methodResult.Id = methodID; + methodResult.NumInStreams = numInStreams; + methodResult.NumOutStreams = 1; + return true; +} + +static bool MakeExeMethod(const CCompressionMethodMode &method, + bool bcj2Filter, CCompressionMethodMode &exeMethod) +{ + exeMethod = method; + if (bcj2Filter) + { + CMethodFull methodFull; + if (!GetMethodFull(k_BCJ2, 4, methodFull)) + return false; + exeMethod.Methods.Insert(0, methodFull); + if (!GetMethodFull(k_LZMA, 1, methodFull)) + return false; + { + CProp property; + property.Id = NCoderPropID::kAlgorithm; + property.Value = kAlgorithmForBCJ2_LZMA; + methodFull.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kMatchFinder; + property.Value = kMatchFinderForBCJ2_LZMA; + methodFull.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kDictionarySize; + property.Value = kDictionaryForBCJ2_LZMA; + methodFull.Properties.Add(property); + } + { + CProp property; + property.Id = NCoderPropID::kNumFastBytes; + property.Value = kNumFastBytesForBCJ2_LZMA; + methodFull.Properties.Add(property); + } + + exeMethod.Methods.Add(methodFull); + exeMethod.Methods.Add(methodFull); + CBind bind; + + bind.OutCoder = 0; + bind.InStream = 0; + + bind.InCoder = 1; + bind.OutStream = 0; + exeMethod.Binds.Add(bind); + + bind.InCoder = 2; + bind.OutStream = 1; + exeMethod.Binds.Add(bind); + + bind.InCoder = 3; + bind.OutStream = 2; + exeMethod.Binds.Add(bind); + } + else + { + CMethodFull methodFull; + if (!GetMethodFull(k_BCJ, 1, methodFull)) + return false; + exeMethod.Methods.Insert(0, methodFull); + CBind bind; + bind.OutCoder = 0; + bind.InStream = 0; + bind.InCoder = 1; + bind.OutStream = 0; + exeMethod.Binds.Add(bind); + } + return true; +} + +static void SplitFilesToGroups( + const CCompressionMethodMode &method, + bool useFilters, bool maxFilter, + const CObjectVector &updateItems, + CObjectVector &groups) +{ + if (method.Methods.Size() != 1 || method.Binds.Size() != 0) + useFilters = false; + groups.Clear(); + groups.Add(CSolidGroup()); + groups.Add(CSolidGroup()); + CSolidGroup &generalGroup = groups[0]; + CSolidGroup &exeGroup = groups[1]; + generalGroup.Method = method; + int i; + for (i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &updateItem = updateItems[i]; + if (!updateItem.NewData) + continue; + if (!updateItem.HasStream()) + continue; + if (useFilters) + { + const UString name = updateItem.Name; + int dotPos = name.ReverseFind(L'.'); + if (dotPos >= 0) + { + UString ext = name.Mid(dotPos + 1); + if (IsExeFile(ext)) + { + exeGroup.Indices.Add(i); + continue; + } + } + } + generalGroup.Indices.Add(i); + } + if (exeGroup.Indices.Size() > 0) + if (!MakeExeMethod(method, maxFilter, exeGroup.Method)) + exeGroup.Method = method; + for (i = 0; i < groups.Size();) + if (groups[i].Indices.Size() == 0) + groups.Delete(i); + else + i++; +} + +static void FromUpdateItemToFileItem(const CUpdateItem &updateItem, + CFileItem &file) +{ + file.Name = NItemName::MakeLegalName(updateItem.Name); + if (updateItem.AttributesAreDefined) + file.SetAttributes(updateItem.Attributes); + + if (updateItem.IsCreationTimeDefined) + file.SetCreationTime(updateItem.CreationTime); + if (updateItem.IsLastWriteTimeDefined) + file.SetLastWriteTime(updateItem.LastWriteTime); + if (updateItem.IsLastAccessTimeDefined) + file.SetLastAccessTime(updateItem.LastAccessTime); + + file.UnPackSize = updateItem.Size; + file.IsDirectory = updateItem.IsDirectory; + file.IsAnti = updateItem.IsAnti; + file.HasStream = updateItem.HasStream(); +} + +static HRESULT Update2( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + const CArchiveDatabaseEx *database, + const CObjectVector &updateItems, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options) +{ + UInt64 numSolidFiles = options.NumSolidFiles; + if (numSolidFiles == 0) + numSolidFiles = 1; + /* + CMyComPtr outStream; + RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream)); + if (!outStream) + return E_NOTIMPL; + */ + + UInt64 startBlockSize = database != 0 ? database->ArchiveInfo.StartPosition: 0; + if (startBlockSize > 0 && !options.RemoveSfxBlock) + { + RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL)); + } + + CRecordVector fileIndexToUpdateIndexMap; + if (database != 0) + { + fileIndexToUpdateIndexMap.Reserve(database->Files.Size()); + for (int i = 0; i < database->Files.Size(); i++) + fileIndexToUpdateIndexMap.Add(-1); + } + int i; + for(i = 0; i < updateItems.Size(); i++) + { + int index = updateItems[i].IndexInArchive; + if (index != -1) + fileIndexToUpdateIndexMap[index] = i; + } + + CRecordVector folderRefs; + if (database != 0) + { + for(i = 0; i < database->Folders.Size(); i++) + { + CNum indexInFolder = 0; + CNum numCopyItems = 0; + CNum numUnPackStreams = database->NumUnPackStreamsVector[i]; + for (CNum fileIndex = database->FolderStartFileIndex[i]; + indexInFolder < numUnPackStreams; fileIndex++) + { + if (database->Files[fileIndex].HasStream) + { + indexInFolder++; + int updateIndex = fileIndexToUpdateIndexMap[fileIndex]; + if (updateIndex >= 0) + if (!updateItems[updateIndex].NewData) + numCopyItems++; + } + } + if (numCopyItems != numUnPackStreams && numCopyItems != 0) + return E_NOTIMPL; // It needs repacking !!! + if (numCopyItems > 0) + folderRefs.Add(i); + } + folderRefs.Sort(CompareFolderRefs, (void *)database); + } + + CArchiveDatabase newDatabase; + + //////////////////////////// + + COutArchive archive; + RINOK(archive.Create(seqOutStream, false)); + RINOK(archive.SkeepPrefixArchiveHeader()); + UInt64 complexity = 0; + for(i = 0; i < folderRefs.Size(); i++) + complexity += database->GetFolderFullPackSize(folderRefs[i]); + UInt64 inSizeForReduce = 0; + for(i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &updateItem = updateItems[i]; + if (updateItem.NewData) + { + complexity += updateItem.Size; + if (numSolidFiles == 1) + { + if (updateItem.Size > inSizeForReduce) + inSizeForReduce = updateItem.Size; + } + else + inSizeForReduce += updateItem.Size; + } + } + RINOK(updateCallback->SetTotal(complexity)); + complexity = 0; + RINOK(updateCallback->SetCompleted(&complexity)); + + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr progress = lps; + lps->Init(updateCallback, true); + + ///////////////////////////////////////// + // Write Copy Items + + for(i = 0; i < folderRefs.Size(); i++) + { + int folderIndex = folderRefs[i]; + + lps->ProgressOffset = complexity; + UInt64 packSize = database->GetFolderFullPackSize(folderIndex); + RINOK(WriteRange(inStream, archive.SeqStream, + database->GetFolderStreamPos(folderIndex, 0), packSize, progress)); + complexity += packSize; + + const CFolder &folder = database->Folders[folderIndex]; + CNum startIndex = database->FolderStartPackStreamIndex[folderIndex]; + for (int j = 0; j < folder.PackStreams.Size(); j++) + { + newDatabase.PackSizes.Add(database->PackSizes[startIndex + j]); + // newDatabase.PackCRCsDefined.Add(database.PackCRCsDefined[startIndex + j]); + // newDatabase.PackCRCs.Add(database.PackCRCs[startIndex + j]); + } + newDatabase.Folders.Add(folder); + + CNum numUnPackStreams = database->NumUnPackStreamsVector[folderIndex]; + newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams); + + CNum indexInFolder = 0; + for (CNum fi = database->FolderStartFileIndex[folderIndex]; + indexInFolder < numUnPackStreams; fi++) + { + CFileItem file = database->Files[fi]; + if (file.HasStream) + { + indexInFolder++; + int updateIndex = fileIndexToUpdateIndexMap[fi]; + if (updateIndex >= 0) + { + const CUpdateItem &updateItem = updateItems[updateIndex]; + if (updateItem.NewProperties) + { + CFileItem file2; + FromUpdateItemToFileItem(updateItem, file2); + file2.UnPackSize = file.UnPackSize; + file2.FileCRC = file.FileCRC; + file2.IsFileCRCDefined = file.IsFileCRCDefined; + file2.HasStream = file.HasStream; + file = file2; + } + } + newDatabase.Files.Add(file); + } + } + } + + ///////////////////////////////////////// + // Compress New Files + + CObjectVector groups; + SplitFilesToGroups(*options.Method, options.UseFilters, options.MaxFilter, + updateItems, groups); + + const UInt32 kMinReduceSize = (1 << 16); + if (inSizeForReduce < kMinReduceSize) + inSizeForReduce = kMinReduceSize; + + for (int groupIndex = 0; groupIndex < groups.Size(); groupIndex++) + { + const CSolidGroup &group = groups[groupIndex]; + int numFiles = group.Indices.Size(); + if (numFiles == 0) + continue; + CRecordVector refItems; + refItems.Reserve(numFiles); + bool sortByType = (numSolidFiles > 1); + for (i = 0; i < numFiles; i++) + refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType)); + refItems.Sort(CompareUpdateItems, (void *)&sortByType); + + CRecordVector indices; + indices.Reserve(numFiles); + + for (i = 0; i < numFiles; i++) + { + UInt32 index = refItems[i].Index; + indices.Add(index); + /* + const CUpdateItem &updateItem = updateItems[index]; + CFileItem file; + if (updateItem.NewProperties) + FromUpdateItemToFileItem(updateItem, file); + else + file = database.Files[updateItem.IndexInArchive]; + if (file.IsAnti || file.IsDirectory) + return E_FAIL; + newDatabase.Files.Add(file); + */ + } + + CEncoder encoder(group.Method); + + for (i = 0; i < numFiles;) + { + UInt64 totalSize = 0; + int numSubFiles; + UString prevExtension; + for (numSubFiles = 0; i + numSubFiles < numFiles && + numSubFiles < numSolidFiles; numSubFiles++) + { + const CUpdateItem &updateItem = updateItems[indices[i + numSubFiles]]; + totalSize += updateItem.Size; + if (totalSize > options.NumSolidBytes) + break; + if (options.SolidExtension) + { + UString ext = updateItem.GetExtension(); + if (numSubFiles == 0) + prevExtension = ext; + else + if (ext.CompareNoCase(prevExtension) != 0) + break; + } + } + if (numSubFiles < 1) + numSubFiles = 1; + + CFolderInStream *inStreamSpec = new CFolderInStream; + CMyComPtr solidInStream(inStreamSpec); + inStreamSpec->Init(updateCallback, &indices[i], numSubFiles); + + CFolder folderItem; + + int startPackIndex = newDatabase.PackSizes.Size(); + RINOK(encoder.Encode( + EXTERNAL_CODECS_LOC_VARS + solidInStream, NULL, &inSizeForReduce, folderItem, + archive.SeqStream, newDatabase.PackSizes, progress)); + + for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) + lps->OutSize += newDatabase.PackSizes[startPackIndex]; + + lps->InSize += folderItem.GetUnPackSize(); + // for() + // newDatabase.PackCRCsDefined.Add(false); + // newDatabase.PackCRCs.Add(0); + + newDatabase.Folders.Add(folderItem); + + CNum numUnPackStreams = 0; + for (int subIndex = 0; subIndex < numSubFiles; subIndex++) + { + const CUpdateItem &updateItem = updateItems[indices[i + subIndex]]; + CFileItem file; + if (updateItem.NewProperties) + FromUpdateItemToFileItem(updateItem, file); + else + file = database->Files[updateItem.IndexInArchive]; + if (file.IsAnti || file.IsDirectory) + return E_FAIL; + + /* + CFileItem &file = newDatabase.Files[ + startFileIndexInDatabase + i + subIndex]; + */ + if (!inStreamSpec->Processed[subIndex]) + { + continue; + // file.Name += L".locked"; + } + + file.FileCRC = inStreamSpec->CRCs[subIndex]; + file.UnPackSize = inStreamSpec->Sizes[subIndex]; + if (file.UnPackSize != 0) + { + file.IsFileCRCDefined = true; + file.HasStream = true; + numUnPackStreams++; + } + else + { + file.IsFileCRCDefined = false; + file.HasStream = false; + } + newDatabase.Files.Add(file); + } + // numUnPackStreams = 0 is very bad case for locked files + // v3.13 doesn't understand it. + newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams); + i += numSubFiles; + } + } + + { + ///////////////////////////////////////// + // Write Empty Files & Folders + + CRecordVector emptyRefs; + for(i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &updateItem = updateItems[i]; + if (updateItem.NewData) + { + if (updateItem.HasStream()) + continue; + } + else + if (updateItem.IndexInArchive != -1) + if (database->Files[updateItem.IndexInArchive].HasStream) + continue; + emptyRefs.Add(i); + } + emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems); + for(i = 0; i < emptyRefs.Size(); i++) + { + const CUpdateItem &updateItem = updateItems[emptyRefs[i]]; + CFileItem file; + if (updateItem.NewProperties) + FromUpdateItemToFileItem(updateItem, file); + else + file = database->Files[updateItem.IndexInArchive]; + newDatabase.Files.Add(file); + } + } + + /* + if (newDatabase.Files.Size() != updateItems.Size()) + return E_FAIL; + */ + + return archive.WriteDatabase(EXTERNAL_CODECS_LOC_VARS + newDatabase, options.HeaderMethod, options.HeaderOptions); +} + +#ifdef _7Z_VOL + +static const UInt64 k_Copy = 0x0; + +static HRESULT WriteVolumeHeader(COutArchive &archive, CFileItem &file, const CUpdateOptions &options) +{ + CCoderInfo coder; + coder.NumInStreams = coder.NumOutStreams = 1; + coder.MethodID = k_Copy; + + CFolder folder; + folder.Coders.Add(coder); + folder.PackStreams.Add(0); + + CNum numUnPackStreams = 0; + if (file.UnPackSize != 0) + { + file.IsFileCRCDefined = true; + file.HasStream = true; + numUnPackStreams++; + } + else + { + throw 1; + file.IsFileCRCDefined = false; + file.HasStream = false; + } + folder.UnPackSizes.Add(file.UnPackSize); + + CArchiveDatabase newDatabase; + newDatabase.Files.Add(file); + newDatabase.Folders.Add(folder); + newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams); + newDatabase.PackSizes.Add(file.UnPackSize); + newDatabase.PackCRCsDefined.Add(false); + newDatabase.PackCRCs.Add(file.FileCRC); + + return archive.WriteDatabase(newDatabase, + options.HeaderMethod, + false, + false); +} + +HRESULT UpdateVolume( + IInStream *inStream, + const CArchiveDatabaseEx *database, + CObjectVector &updateItems, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options) +{ + if (updateItems.Size() != 1) + return E_NOTIMPL; + + CMyComPtr volumeCallback; + RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback)); + if (!volumeCallback) + return E_NOTIMPL; + + CMyComPtr fileStream; + HRESULT result = updateCallback->GetStream(0, &fileStream); + if (result != S_OK && result != S_FALSE) + return result; + if (result == S_FALSE) + return E_FAIL; + + CFileItem file; + + const CUpdateItem &updateItem = updateItems[0]; + if (updateItem.NewProperties) + FromUpdateItemToFileItem(updateItem, file); + else + file = database->Files[updateItem.IndexInArchive]; + if (file.IsAnti || file.IsDirectory) + return E_FAIL; + + UInt64 complexity = 0; + file.IsStartPosDefined = true; + file.StartPos = 0; + for (UInt64 volumeIndex = 0; true; volumeIndex++) + { + UInt64 volSize; + RINOK(volumeCallback->GetVolumeSize(volumeIndex, &volSize)); + UInt64 pureSize = COutArchive::GetVolPureSize(volSize, file.Name.Length(), true); + CMyComPtr volumeStream; + RINOK(volumeCallback->GetVolumeStream(volumeIndex, &volumeStream)); + + COutArchive archive; + RINOK(archive.Create(volumeStream, true)); + RINOK(archive.SkeepPrefixArchiveHeader()); + + CSequentialInStreamWithCRC *inCrcStreamSpec = new CSequentialInStreamWithCRC; + CMyComPtr inCrcStream = inCrcStreamSpec; + inCrcStreamSpec->Init(fileStream); + + RINOK(WriteRange(inCrcStream, volumeStream, pureSize, updateCallback, complexity)); + file.UnPackSize = inCrcStreamSpec->GetSize(); + if (file.UnPackSize == 0) + break; + file.FileCRC = inCrcStreamSpec->GetCRC(); + RINOK(WriteVolumeHeader(archive, file, options)); + file.StartPos += file.UnPackSize; + if (file.UnPackSize < pureSize) + break; + } + return S_OK; +} + +class COutVolumeStream: + public ISequentialOutStream, + public CMyUnknownImp +{ + int _volIndex; + UInt64 _volSize; + UInt64 _curPos; + CMyComPtr _volumeStream; + COutArchive _archive; + CCRC _crc; + +public: + MY_UNKNOWN_IMP + + CFileItem _file; + CUpdateOptions _options; + CMyComPtr VolumeCallback; + void Init(IArchiveUpdateCallback2 *volumeCallback, + const UString &name) + { + _file.Name = name; + _file.IsStartPosDefined = true; + _file.StartPos = 0; + + VolumeCallback = volumeCallback; + _volIndex = 0; + _volSize = 0; + } + + HRESULT Flush(); + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +HRESULT COutVolumeStream::Flush() +{ + if (_volumeStream) + { + _file.UnPackSize = _curPos; + _file.FileCRC = _crc.GetDigest(); + RINOK(WriteVolumeHeader(_archive, _file, _options)); + _archive.Close(); + _volumeStream.Release(); + _file.StartPos += _file.UnPackSize; + } + return S_OK; +} + +STDMETHODIMP COutVolumeStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + while(size > 0) + { + if (!_volumeStream) + { + RINOK(VolumeCallback->GetVolumeSize(_volIndex, &_volSize)); + RINOK(VolumeCallback->GetVolumeStream(_volIndex, &_volumeStream)); + _volIndex++; + _curPos = 0; + RINOK(_archive.Create(_volumeStream, true)); + RINOK(_archive.SkeepPrefixArchiveHeader()); + _crc.Init(); + continue; + } + UInt64 pureSize = COutArchive::GetVolPureSize(_volSize, _file.Name.Length()); + UInt32 curSize = (UInt32)MyMin(UInt64(size), pureSize - _curPos); + + _crc.Update(data, curSize); + UInt32 realProcessed; + RINOK(_volumeStream->Write(data, curSize, &realProcessed)) + data = (void *)((Byte *)data + realProcessed); + size -= realProcessed; + if(processedSize != NULL) + *processedSize += realProcessed; + _curPos += realProcessed; + if (realProcessed != curSize && realProcessed == 0) + return E_FAIL; + if (_curPos == pureSize) + { + RINOK(Flush()); + } + } + return S_OK; +} + +#endif + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + const CArchiveDatabaseEx *database, + const CObjectVector &updateItems, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options) +{ + #ifdef _7Z_VOL + if (seqOutStream) + #endif + return Update2( + EXTERNAL_CODECS_LOC_VARS + inStream, database, updateItems, + seqOutStream, updateCallback, options); + #ifdef _7Z_VOL + if (options.VolumeMode) + return UpdateVolume(inStream, database, updateItems, + seqOutStream, updateCallback, options); + COutVolumeStream *volStreamSpec = new COutVolumeStream; + CMyComPtr volStream = volStreamSpec; + CMyComPtr volumeCallback; + RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback)); + if (!volumeCallback) + return E_NOTIMPL; + volStreamSpec->Init(volumeCallback, L"a.7z"); + volStreamSpec->_options = options; + RINOK(Update2(inStream, database, updateItems, + volStream, updateCallback, options)); + return volStreamSpec->Flush(); + #endif +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.h new file mode 100644 index 0000000..ac1b5b6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/7zUpdate.h @@ -0,0 +1,80 @@ +// 7zUpdate.h + +#ifndef __7Z_UPDATE_H +#define __7Z_UPDATE_H + +#include "7zIn.h" +#include "7zOut.h" +#include "7zCompressionMode.h" + +#include "../IArchive.h" + +namespace NArchive { +namespace N7z { + +struct CUpdateItem +{ + bool NewData; + bool NewProperties; + int IndexInArchive; + int IndexInClient; + + UInt32 Attributes; + FILETIME CreationTime; + FILETIME LastWriteTime; + FILETIME LastAccessTime; + + UInt64 Size; + UString Name; + + bool IsAnti; + bool IsDirectory; + + bool IsCreationTimeDefined; + bool IsLastWriteTimeDefined; + bool IsLastAccessTimeDefined; + bool AttributesAreDefined; + + bool HasStream() const + { return !IsDirectory && !IsAnti && Size != 0; } + CUpdateItem(): + IsAnti(false), + AttributesAreDefined(false), + IsCreationTimeDefined(false), + IsLastWriteTimeDefined(false), + IsLastAccessTimeDefined(false) + {} + void SetDirectoryStatusFromAttributes() + { IsDirectory = ((Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0); }; + + int GetExtensionPos() const; + UString GetExtension() const; +}; + +struct CUpdateOptions +{ + const CCompressionMethodMode *Method; + const CCompressionMethodMode *HeaderMethod; + bool UseFilters; + bool MaxFilter; + + CHeaderOptions HeaderOptions; + + UInt64 NumSolidFiles; + UInt64 NumSolidBytes; + bool SolidExtension; + bool RemoveSfxBlock; + bool VolumeMode; +}; + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + const CArchiveDatabaseEx *database, + const CObjectVector &updateItems, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options); +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.h new file mode 100644 index 0000000..2e4be10 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/7z/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" +#include "../../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive.def b/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive.def new file mode 100644 index 0000000..55b530b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive.def @@ -0,0 +1,6 @@ +EXPORTS + CreateObject PRIVATE + GetHandlerProperty PRIVATE + GetNumberOfFormats PRIVATE + GetHandlerProperty2 PRIVATE + CreateObject PRIVATE diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive2.def b/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive2.def new file mode 100644 index 0000000..885d39d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Archive2.def @@ -0,0 +1,9 @@ +EXPORTS + CreateObject PRIVATE + GetHandlerProperty PRIVATE + GetNumberOfFormats PRIVATE + GetHandlerProperty2 PRIVATE + CreateObject PRIVATE + GetNumberOfMethods PRIVATE + GetMethodProperty PRIVATE + SetLargePageMode PRIVATE diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/ArchiveExports.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/ArchiveExports.cpp new file mode 100644 index 0000000..a60303a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/ArchiveExports.cpp @@ -0,0 +1,130 @@ +// ArchiveExports.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" +#include "../../Common/Types.h" +#include "../../Windows/PropVariant.h" +#include "../Common/RegisterArc.h" + +#include "IArchive.h" +#include "../ICoder.h" +#include "../IPassword.h" + +static const unsigned int kNumArcsMax = 32; +static unsigned int g_NumArcs = 0; +static const CArcInfo *g_Arcs[kNumArcsMax]; +void RegisterArc(const CArcInfo *arcInfo) +{ + if (g_NumArcs < kNumArcsMax) + g_Arcs[g_NumArcs++] = arcInfo; +} + +DEFINE_GUID(CLSID_CArchiveHandler, +0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00); + +#define CLS_ARC_ID_ITEM(cls) ((cls).Data4[5]) + +static inline HRESULT SetPropString(const char *s, unsigned int size, PROPVARIANT *value) +{ + if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0) + value->vt = VT_BSTR; + return S_OK; +} + +static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value) +{ + return SetPropString((const char *)&guid, sizeof(GUID), value); +} + +int FindFormatCalssId(const GUID *clsID) +{ + GUID cls = *clsID; + CLS_ARC_ID_ITEM(cls) = 0; + if (cls != CLSID_CArchiveHandler) + return -1; + Byte id = CLS_ARC_ID_ITEM(*clsID); + for (UInt32 i = 0; i < g_NumArcs; i++) + if (g_Arcs[i]->ClassId == id) + return i; + return -1; +} + +STDAPI CreateArchiver(const GUID *clsid, const GUID *iid, void **outObject) +{ + COM_TRY_BEGIN + { + int needIn = (*iid == IID_IInArchive); + int needOut = (*iid == IID_IOutArchive); + if (!needIn && !needOut) + return E_NOINTERFACE; + int formatIndex = FindFormatCalssId(clsid); + if (formatIndex < 0) + return CLASS_E_CLASSNOTAVAILABLE; + + const CArcInfo &arc = *g_Arcs[formatIndex]; + if (needIn) + { + *outObject = arc.CreateInArchive(); + ((IInArchive *)*outObject)->AddRef(); + } + else + { + if (!arc.CreateOutArchive) + return CLASS_E_CLASSNOTAVAILABLE; + *outObject = arc.CreateOutArchive(); + ((IOutArchive *)*outObject)->AddRef(); + } + } + COM_TRY_END + return S_OK; +} + +STDAPI GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT *value) +{ + if (formatIndex >= g_NumArcs) + return E_INVALIDARG; + const CArcInfo &arc = *g_Arcs[formatIndex]; + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case NArchive::kName: + prop = arc.Name; + break; + case NArchive::kClassID: + { + GUID clsId = CLSID_CArchiveHandler; + CLS_ARC_ID_ITEM(clsId) = arc.ClassId; + return SetPropGUID(clsId, value); + } + case NArchive::kExtension: + if (arc.Ext != 0) + prop = arc.Ext; + break; + case NArchive::kAddExtension: + if (arc.AddExt != 0) + prop = arc.AddExt; + break; + case NArchive::kUpdate: + prop = (bool)(arc.CreateOutArchive != 0); + break; + case NArchive::kKeepName: + prop = arc.KeepName; + break; + case NArchive::kStartSignature: + return SetPropString((const char *)arc.Signature, arc.SignatureSize, value); + } + prop.Detach(value); + return S_OK; +} + +STDAPI GetHandlerProperty(PROPID propID, PROPVARIANT *value) +{ + return GetHandlerProperty2(0, propID, value); +} + +STDAPI GetNumberOfFormats(UINT32 *numFormats) +{ + *numFormats = g_NumArcs; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp new file mode 100644 index 0000000..d11e9e6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp @@ -0,0 +1,121 @@ +// CoderMixer2.cpp + +#include "StdAfx.h" + +#include "CoderMixer2.h" + +namespace NCoderMixer { + +CBindReverseConverter::CBindReverseConverter(const CBindInfo &srcBindInfo): + _srcBindInfo(srcBindInfo) +{ + srcBindInfo.GetNumStreams(NumSrcInStreams, _numSrcOutStreams); + + UInt32 j; + for (j = 0; j < NumSrcInStreams; j++) + { + _srcInToDestOutMap.Add(0); + DestOutToSrcInMap.Add(0); + } + for (j = 0; j < _numSrcOutStreams; j++) + { + _srcOutToDestInMap.Add(0); + _destInToSrcOutMap.Add(0); + } + + UInt32 destInOffset = 0; + UInt32 destOutOffset = 0; + UInt32 srcInOffset = NumSrcInStreams; + UInt32 srcOutOffset = _numSrcOutStreams; + + for (int i = srcBindInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderStreamsInfo &srcCoderInfo = srcBindInfo.Coders[i]; + + srcInOffset -= srcCoderInfo.NumInStreams; + srcOutOffset -= srcCoderInfo.NumOutStreams; + + UInt32 j; + for (j = 0; j < srcCoderInfo.NumInStreams; j++, destOutOffset++) + { + UInt32 index = srcInOffset + j; + _srcInToDestOutMap[index] = destOutOffset; + DestOutToSrcInMap[destOutOffset] = index; + } + for (j = 0; j < srcCoderInfo.NumOutStreams; j++, destInOffset++) + { + UInt32 index = srcOutOffset + j; + _srcOutToDestInMap[index] = destInOffset; + _destInToSrcOutMap[destInOffset] = index; + } + } +} + +void CBindReverseConverter::CreateReverseBindInfo(CBindInfo &destBindInfo) +{ + destBindInfo.Coders.Clear(); + destBindInfo.BindPairs.Clear(); + destBindInfo.InStreams.Clear(); + destBindInfo.OutStreams.Clear(); + + int i; + for (i = _srcBindInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderStreamsInfo &srcCoderInfo = _srcBindInfo.Coders[i]; + CCoderStreamsInfo destCoderInfo; + destCoderInfo.NumInStreams = srcCoderInfo.NumOutStreams; + destCoderInfo.NumOutStreams = srcCoderInfo.NumInStreams; + destBindInfo.Coders.Add(destCoderInfo); + } + for (i = _srcBindInfo.BindPairs.Size() - 1; i >= 0; i--) + { + const CBindPair &srcBindPair = _srcBindInfo.BindPairs[i]; + CBindPair destBindPair; + destBindPair.InIndex = _srcOutToDestInMap[srcBindPair.OutIndex]; + destBindPair.OutIndex = _srcInToDestOutMap[srcBindPair.InIndex]; + destBindInfo.BindPairs.Add(destBindPair); + } + for (i = 0; i < _srcBindInfo.InStreams.Size(); i++) + destBindInfo.OutStreams.Add(_srcInToDestOutMap[_srcBindInfo.InStreams[i]]); + for (i = 0; i < _srcBindInfo.OutStreams.Size(); i++) + destBindInfo.InStreams.Add(_srcOutToDestInMap[_srcBindInfo.OutStreams[i]]); +} + +CCoderInfo2::CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams): + NumInStreams(numInStreams), + NumOutStreams(numOutStreams) +{ + InSizes.Reserve(NumInStreams); + InSizePointers.Reserve(NumInStreams); + OutSizePointers.Reserve(NumOutStreams); + OutSizePointers.Reserve(NumOutStreams); +} + +static void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, + CRecordVector &sizePointers, UInt32 numItems) +{ + sizes.Clear(); + sizePointers.Clear(); + for(UInt32 i = 0; i < numItems; i++) + { + if (srcSizes == 0 || srcSizes[i] == NULL) + { + sizes.Add(0); + sizePointers.Add(NULL); + } + else + { + sizes.Add(*srcSizes[i]); + sizePointers.Add(&sizes.Back()); + } + } +} + +void CCoderInfo2::SetCoderInfo(const UInt64 **inSizes, + const UInt64 **outSizes) +{ + SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); + SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); +} + +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.h new file mode 100644 index 0000000..be68c68 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.h @@ -0,0 +1,174 @@ +// CoderMixer2.h + +#ifndef __CODER_MIXER2_H +#define __CODER_MIXER2_H + +#include "../../../Common/MyVector.h" +#include "../../../Common/Types.h" +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" + +namespace NCoderMixer { + +struct CBindPair +{ + UInt32 InIndex; + UInt32 OutIndex; +}; + +struct CCoderStreamsInfo +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; +}; + +struct CBindInfo +{ + CRecordVector Coders; + CRecordVector BindPairs; + CRecordVector InStreams; + CRecordVector OutStreams; + + void Clear() + { + Coders.Clear(); + BindPairs.Clear(); + InStreams.Clear(); + OutStreams.Clear(); + } + + /* + UInt32 GetCoderStartOutStream(UInt32 coderIndex) const + { + UInt32 numOutStreams = 0; + for (UInt32 i = 0; i < coderIndex; i++) + numOutStreams += Coders[i].NumOutStreams; + return numOutStreams; + } + */ + + + void GetNumStreams(UInt32 &numInStreams, UInt32 &numOutStreams) const + { + numInStreams = 0; + numOutStreams = 0; + for (int i = 0; i < Coders.Size(); i++) + { + const CCoderStreamsInfo &coderStreamsInfo = Coders[i]; + numInStreams += coderStreamsInfo.NumInStreams; + numOutStreams += coderStreamsInfo.NumOutStreams; + } + } + + int FindBinderForInStream(UInt32 inStream) const + { + for (int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].InIndex == inStream) + return i; + return -1; + } + int FindBinderForOutStream(UInt32 outStream) const + { + for (int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].OutIndex == outStream) + return i; + return -1; + } + + UInt32 GetCoderInStreamIndex(UInt32 coderIndex) const + { + UInt32 streamIndex = 0; + for (UInt32 i = 0; i < coderIndex; i++) + streamIndex += Coders[i].NumInStreams; + return streamIndex; + } + + UInt32 GetCoderOutStreamIndex(UInt32 coderIndex) const + { + UInt32 streamIndex = 0; + for (UInt32 i = 0; i < coderIndex; i++) + streamIndex += Coders[i].NumOutStreams; + return streamIndex; + } + + + void FindInStream(UInt32 streamIndex, UInt32 &coderIndex, + UInt32 &coderStreamIndex) const + { + for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) + { + UInt32 curSize = Coders[coderIndex].NumInStreams; + if (streamIndex < curSize) + { + coderStreamIndex = streamIndex; + return; + } + streamIndex -= curSize; + } + throw 1; + } + void FindOutStream(UInt32 streamIndex, UInt32 &coderIndex, + UInt32 &coderStreamIndex) const + { + for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) + { + UInt32 curSize = Coders[coderIndex].NumOutStreams; + if (streamIndex < curSize) + { + coderStreamIndex = streamIndex; + return; + } + streamIndex -= curSize; + } + throw 1; + } +}; + +class CBindReverseConverter +{ + UInt32 _numSrcOutStreams; + NCoderMixer::CBindInfo _srcBindInfo; + CRecordVector _srcInToDestOutMap; + CRecordVector _srcOutToDestInMap; + CRecordVector _destInToSrcOutMap; +public: + UInt32 NumSrcInStreams; + CRecordVector DestOutToSrcInMap; + + CBindReverseConverter(const NCoderMixer::CBindInfo &srcBindInfo); + void CreateReverseBindInfo(NCoderMixer::CBindInfo &destBindInfo); +}; + +struct CCoderInfo2 +{ + CMyComPtr Coder; + CMyComPtr Coder2; + UInt32 NumInStreams; + UInt32 NumOutStreams; + + CRecordVector InSizes; + CRecordVector OutSizes; + CRecordVector InSizePointers; + CRecordVector OutSizePointers; + + CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams); + void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); + + HRESULT QueryInterface(REFGUID iid, void** pp) const + { + IUnknown *p = Coder ? (IUnknown *)Coder : (IUnknown *)Coder2; + return p->QueryInterface(iid, pp); + } +}; + +class CCoderMixer2 +{ +public: + virtual HRESULT SetBindInfo(const CBindInfo &bindInfo) = 0; + virtual void ReInit() = 0; + virtual void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) = 0; +}; + +} +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.cpp new file mode 100644 index 0000000..2ef1fa9 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.cpp @@ -0,0 +1,228 @@ +// CoderMixer2MT.cpp + +#include "StdAfx.h" + +#include "CoderMixer2MT.h" + +namespace NCoderMixer { + +CCoder2::CCoder2(UInt32 numInStreams, UInt32 numOutStreams): + CCoderInfo2(numInStreams, numOutStreams) +{ + InStreams.Reserve(NumInStreams); + InStreamPointers.Reserve(NumInStreams); + OutStreams.Reserve(NumOutStreams); + OutStreamPointers.Reserve(NumOutStreams); +} + +void CCoder2::Execute() { Code(NULL); } + +void CCoder2::Code(ICompressProgressInfo *progress) +{ + InStreamPointers.Clear(); + OutStreamPointers.Clear(); + UInt32 i; + for (i = 0; i < NumInStreams; i++) + { + if (InSizePointers[i] != NULL) + InSizePointers[i] = &InSizes[i]; + InStreamPointers.Add((ISequentialInStream *)InStreams[i]); + } + for (i = 0; i < NumOutStreams; i++) + { + if (OutSizePointers[i] != NULL) + OutSizePointers[i] = &OutSizes[i]; + OutStreamPointers.Add((ISequentialOutStream *)OutStreams[i]); + } + if (Coder) + Result = Coder->Code(InStreamPointers[0], OutStreamPointers[0], + InSizePointers[0], OutSizePointers[0], progress); + else + Result = Coder2->Code(&InStreamPointers.Front(), &InSizePointers.Front(), NumInStreams, + &OutStreamPointers.Front(), &OutSizePointers.Front(), NumOutStreams, progress); + { + int i; + for (i = 0; i < InStreams.Size(); i++) + InStreams[i].Release(); + for (i = 0; i < OutStreams.Size(); i++) + OutStreams[i].Release(); + } +} + +static void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, + CRecordVector &sizePointers, UInt32 numItems) +{ + sizes.Clear(); + sizePointers.Clear(); + for(UInt32 i = 0; i < numItems; i++) + { + if (srcSizes == 0 || srcSizes[i] == NULL) + { + sizes.Add(0); + sizePointers.Add(NULL); + } + else + { + sizes.Add(*srcSizes[i]); + sizePointers.Add(&sizes.Back()); + } + } +} + + +void CCoder2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) +{ + SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); + SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); +} + +////////////////////////////////////// +// CCoderMixer2MT + +HRESULT CCoderMixer2MT::SetBindInfo(const CBindInfo &bindInfo) +{ + _bindInfo = bindInfo; + _streamBinders.Clear(); + for(int i = 0; i < _bindInfo.BindPairs.Size(); i++) + { + _streamBinders.Add(CStreamBinder()); + RINOK(_streamBinders.Back().CreateEvents()); + } + return S_OK; +} + +void CCoderMixer2MT::AddCoderCommon() +{ + const CCoderStreamsInfo &c = _bindInfo.Coders[_coders.Size()]; + CCoder2 threadCoderInfo(c.NumInStreams, c.NumOutStreams); + _coders.Add(threadCoderInfo); +} + +void CCoderMixer2MT::AddCoder(ICompressCoder *coder) +{ + AddCoderCommon(); + _coders.Back().Coder = coder; +} + +void CCoderMixer2MT::AddCoder2(ICompressCoder2 *coder) +{ + AddCoderCommon(); + _coders.Back().Coder2 = coder; +} + + +void CCoderMixer2MT::ReInit() +{ + for(int i = 0; i < _streamBinders.Size(); i++) + _streamBinders[i].ReInit(); +} + + +HRESULT CCoderMixer2MT::Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams) +{ + /* + if (_coders.Size() != _bindInfo.Coders.Size()) + throw 0; + */ + int i; + for(i = 0; i < _coders.Size(); i++) + { + CCoder2 &coderInfo = _coders[i]; + const CCoderStreamsInfo &coderStreamsInfo = _bindInfo.Coders[i]; + coderInfo.InStreams.Clear(); + UInt32 j; + for(j = 0; j < coderStreamsInfo.NumInStreams; j++) + coderInfo.InStreams.Add(NULL); + coderInfo.OutStreams.Clear(); + for(j = 0; j < coderStreamsInfo.NumOutStreams; j++) + coderInfo.OutStreams.Add(NULL); + } + + for(i = 0; i < _bindInfo.BindPairs.Size(); i++) + { + const CBindPair &bindPair = _bindInfo.BindPairs[i]; + UInt32 inCoderIndex, inCoderStreamIndex; + UInt32 outCoderIndex, outCoderStreamIndex; + _bindInfo.FindInStream(bindPair.InIndex, inCoderIndex, inCoderStreamIndex); + _bindInfo.FindOutStream(bindPair.OutIndex, outCoderIndex, outCoderStreamIndex); + + _streamBinders[i].CreateStreams( + &_coders[inCoderIndex].InStreams[inCoderStreamIndex], + &_coders[outCoderIndex].OutStreams[outCoderStreamIndex]); + } + + for(i = 0; i < _bindInfo.InStreams.Size(); i++) + { + UInt32 inCoderIndex, inCoderStreamIndex; + _bindInfo.FindInStream(_bindInfo.InStreams[i], inCoderIndex, inCoderStreamIndex); + _coders[inCoderIndex].InStreams[inCoderStreamIndex] = inStreams[i]; + } + + for(i = 0; i < _bindInfo.OutStreams.Size(); i++) + { + UInt32 outCoderIndex, outCoderStreamIndex; + _bindInfo.FindOutStream(_bindInfo.OutStreams[i], outCoderIndex, outCoderStreamIndex); + _coders[outCoderIndex].OutStreams[outCoderStreamIndex] = outStreams[i]; + } + return S_OK; +} + +HRESULT CCoderMixer2MT::ReturnIfError(HRESULT code) +{ + for (int i = 0; i < _coders.Size(); i++) + if (_coders[i].Result == code) + return code; + return S_OK; +} + +STDMETHODIMP CCoderMixer2MT::Code(ISequentialInStream **inStreams, + const UInt64 ** /* inSizes */, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 ** /* outSizes */, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + if (numInStreams != (UInt32)_bindInfo.InStreams.Size() || + numOutStreams != (UInt32)_bindInfo.OutStreams.Size()) + return E_INVALIDARG; + + Init(inStreams, outStreams); + + int i; + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + { + RINOK(_coders[i].Create()); + } + + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + _coders[i].Start(); + + _coders[_progressCoderIndex].Code(progress); + + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + _coders[i].WaitFinish(); + + RINOK(ReturnIfError(E_ABORT)); + RINOK(ReturnIfError(E_OUTOFMEMORY)); + RINOK(ReturnIfError(S_FALSE)); + + for (i = 0; i < _coders.Size(); i++) + { + HRESULT result = _coders[i].Result; + if (result != S_OK && result != E_FAIL) + return result; + } + for (i = 0; i < _coders.Size(); i++) + { + HRESULT result = _coders[i].Result; + if (result != S_OK) + return result; + } + return S_OK; +} + +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.h new file mode 100644 index 0000000..505f1a8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.h @@ -0,0 +1,80 @@ +// CoderMixer2MT.h + +#ifndef __CODER_MIXER2_MT_H +#define __CODER_MIXER2_MT_H + +#include "CoderMixer2.h" +#include "../../../Common/MyCom.h" +#include "../../Common/StreamBinder.h" +#include "../../Common/VirtThread.h" + +namespace NCoderMixer { + +struct CCoder2: public CCoderInfo2, public CVirtThread +{ + HRESULT Result; + CObjectVector< CMyComPtr > InStreams; + CObjectVector< CMyComPtr > OutStreams; + CRecordVector InStreamPointers; + CRecordVector OutStreamPointers; + + CCoder2(UInt32 numInStreams, UInt32 numOutStreams); + void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); + virtual void Execute(); + void Code(ICompressProgressInfo *progress); +}; + + +/* + SetBindInfo() + for each coder + AddCoder[2]() + SetProgressIndex(UInt32 coderIndex); + + for each file + { + ReInit() + for each coder + SetCoderInfo + Code + } +*/ + +class CCoderMixer2MT: + public ICompressCoder2, + public CCoderMixer2, + public CMyUnknownImp +{ + CBindInfo _bindInfo; + CObjectVector _streamBinders; + int _progressCoderIndex; + + void AddCoderCommon(); + HRESULT Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams); + HRESULT ReturnIfError(HRESULT code); +public: + CObjectVector _coders; + MY_UNKNOWN_IMP + + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); + + HRESULT SetBindInfo(const CBindInfo &bindInfo); + void AddCoder(ICompressCoder *coder); + void AddCoder2(ICompressCoder2 *coder); + void SetProgressCoderIndex(int coderIndex) { _progressCoderIndex = coderIndex; } + + void ReInit(); + void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) + { _coders[coderIndex].SetCoderInfo(inSizes, outSizes); } + UInt64 GetWriteProcessedSize(UInt32 binderIndex) const + { return _streamBinders[binderIndex].ProcessedSize; } +}; + +} +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.cpp new file mode 100644 index 0000000..a974b54 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.cpp @@ -0,0 +1,15 @@ +// CrossThreadProgress.cpp + +#include "StdAfx.h" + +#include "CrossThreadProgress.h" + +STDMETHODIMP CCrossThreadProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + InSize = inSize; + OutSize = outSize; + ProgressEvent.Set(); + WaitEvent.Lock(); + return Result; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.h new file mode 100644 index 0000000..b5422a3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.h @@ -0,0 +1,37 @@ +// CrossThreadProgress.h + +#ifndef __CROSSTHREADPROGRESS_H +#define __CROSSTHREADPROGRESS_H + +#include "../../ICoder.h" +#include "../../../Windows/Synchronization.h" +#include "../../../Common/MyCom.h" + +class CCrossThreadProgress: + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + const UInt64 *InSize; + const UInt64 *OutSize; + HRESULT Result; + NWindows::NSynchronization::CAutoResetEvent ProgressEvent; + NWindows::NSynchronization::CAutoResetEvent WaitEvent; + + HRes Create() + { + RINOK(ProgressEvent.CreateIfNotCreated()); + return WaitEvent.CreateIfNotCreated(); + } + void Init() + { + ProgressEvent.Reset(); + WaitEvent.Reset(); + } + + MY_UNKNOWN_IMP + + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp new file mode 100644 index 0000000..54bcfec --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp @@ -0,0 +1,22 @@ +// DummyOutStream.cpp + +#include "StdAfx.h" + +#include "DummyOutStream.h" + +STDMETHODIMP CDummyOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result; + if(!_stream) + { + realProcessedSize = size; + result = S_OK; + } + else + result = _stream->Write(data, size, &realProcessedSize); + _size += realProcessedSize; + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.h new file mode 100644 index 0000000..d19b311 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.h @@ -0,0 +1,23 @@ +// DummyOutStream.h + +#ifndef __DUMMYOUTSTREAM_H +#define __DUMMYOUTSTREAM_H + +#include "../../IStream.h" +#include "Common/MyCom.h" + +class CDummyOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr _stream; + UInt64 _size; +public: + void SetStream(ISequentialOutStream *outStream) { _stream = outStream; } + void Init() { _size = 0; } + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + UInt64 GetSize() const { return _size; } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp new file mode 100644 index 0000000..0dcf449 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp @@ -0,0 +1,618 @@ +// HandlerOutCommon.cpp + +#include "StdAfx.h" + +#include "HandlerOut.h" +#include "../../../Windows/PropVariant.h" +#include "../../../Common/StringToInt.h" +#include "../../ICoder.h" +#include "../Common/ParseProperties.h" + +#ifdef COMPRESS_MT +#include "../../../Windows/System.h" +#endif + +using namespace NWindows; + +namespace NArchive { + +static const wchar_t *kCopyMethod = L"Copy"; +static const wchar_t *kLZMAMethodName = L"LZMA"; +static const wchar_t *kLZMA2MethodName = L"LZMA2"; +static const wchar_t *kBZip2MethodName = L"BZip2"; +static const wchar_t *kPpmdMethodName = L"PPMd"; +static const wchar_t *kDeflateMethodName = L"Deflate"; +static const wchar_t *kDeflate64MethodName = L"Deflate64"; + +static const wchar_t *kLzmaMatchFinderX1 = L"HC4"; +static const wchar_t *kLzmaMatchFinderX5 = L"BT4"; + +static const UInt32 kLzmaAlgoX1 = 0; +static const UInt32 kLzmaAlgoX5 = 1; + +static const UInt32 kLzmaDicSizeX1 = 1 << 16; +static const UInt32 kLzmaDicSizeX3 = 1 << 20; +static const UInt32 kLzmaDicSizeX5 = 1 << 24; +static const UInt32 kLzmaDicSizeX7 = 1 << 25; +static const UInt32 kLzmaDicSizeX9 = 1 << 26; + +static const UInt32 kLzmaFastBytesX1 = 32; +static const UInt32 kLzmaFastBytesX7 = 64; + +static const UInt32 kPpmdMemSizeX1 = (1 << 22); +static const UInt32 kPpmdMemSizeX5 = (1 << 24); +static const UInt32 kPpmdMemSizeX7 = (1 << 26); +static const UInt32 kPpmdMemSizeX9 = (192 << 20); + +static const UInt32 kPpmdOrderX1 = 4; +static const UInt32 kPpmdOrderX5 = 6; +static const UInt32 kPpmdOrderX7 = 16; +static const UInt32 kPpmdOrderX9 = 32; + +static const UInt32 kDeflateAlgoX1 = 0; +static const UInt32 kDeflateAlgoX5 = 1; + +static const UInt32 kDeflateFastBytesX1 = 32; +static const UInt32 kDeflateFastBytesX7 = 64; +static const UInt32 kDeflateFastBytesX9 = 128; + +static const UInt32 kDeflatePassesX1 = 1; +static const UInt32 kDeflatePassesX7 = 3; +static const UInt32 kDeflatePassesX9 = 10; + +static const UInt32 kBZip2NumPassesX1 = 1; +static const UInt32 kBZip2NumPassesX7 = 2; +static const UInt32 kBZip2NumPassesX9 = 7; + +static const UInt32 kBZip2DicSizeX1 = 100000; +static const UInt32 kBZip2DicSizeX3 = 500000; +static const UInt32 kBZip2DicSizeX5 = 900000; + +static const wchar_t *kDefaultMethodName = kLZMAMethodName; + +static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2"; +static const UInt32 kDictionaryForHeaders = 1 << 20; +static const UInt32 kNumFastBytesForHeaders = 273; +static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5; + +static bool AreEqual(const UString &methodName, const wchar_t *s) + { return (methodName.CompareNoCase(s) == 0); } + +static inline bool IsLZMAMethod(const UString &methodName) +{ + return + AreEqual(methodName, kLZMAMethodName) || + AreEqual(methodName, kLZMA2MethodName); +} + +static inline bool IsBZip2Method(const UString &methodName) + { return AreEqual(methodName, kBZip2MethodName); } + +static inline bool IsPpmdMethod(const UString &methodName) + { return AreEqual(methodName, kPpmdMethodName); } + +static inline bool IsDeflateMethod(const UString &methodName) +{ + return + AreEqual(methodName, kDeflateMethodName) || + AreEqual(methodName, kDeflate64MethodName); +} + +struct CNameToPropID +{ + PROPID PropID; + VARTYPE VarType; + const wchar_t *Name; +}; + +CNameToPropID g_NameToPropID[] = +{ + { NCoderPropID::kOrder, VT_UI4, L"O" }, + { NCoderPropID::kPosStateBits, VT_UI4, L"PB" }, + { NCoderPropID::kLitContextBits, VT_UI4, L"LC" }, + { NCoderPropID::kLitPosBits, VT_UI4, L"LP" }, + { NCoderPropID::kEndMarker, VT_BOOL, L"eos" }, + + { NCoderPropID::kNumPasses, VT_UI4, L"Pass" }, + { NCoderPropID::kNumFastBytes, VT_UI4, L"fb" }, + { NCoderPropID::kMatchFinderCycles, VT_UI4, L"mc" }, + { NCoderPropID::kAlgorithm, VT_UI4, L"a" }, + { NCoderPropID::kMatchFinder, VT_BSTR, L"mf" }, + { NCoderPropID::kNumThreads, VT_UI4, L"mt" } +}; + +static bool ConvertProperty(PROPVARIANT srcProp, VARTYPE varType, NCOM::CPropVariant &destProp) +{ + if (varType == srcProp.vt) + { + destProp = srcProp; + return true; + } + if (varType == VT_UI1) + { + if (srcProp.vt == VT_UI4) + { + UInt32 value = srcProp.ulVal; + if (value > 0xFF) + return false; + destProp = (Byte)value; + return true; + } + } + else if (varType == VT_BOOL) + { + bool res; + if (SetBoolProperty(res, srcProp) != S_OK) + return false; + destProp = res; + return true; + } + return false; +} + +static int FindPropIdFromStringName(const UString &name) +{ + for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++) + if (name.CompareNoCase(g_NameToPropID[i].Name) == 0) + return i; + return -1; +} + +static void SetOneMethodProp(COneMethodInfo &oneMethodInfo, PROPID propID, + const NWindows::NCOM::CPropVariant &value) +{ + for (int j = 0; j < oneMethodInfo.Properties.Size(); j++) + if (oneMethodInfo.Properties[j].Id == propID) + return; + CProp property; + property.Id = propID; + property.Value = value; + oneMethodInfo.Properties.Add(property); +} + +void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo + #ifdef COMPRESS_MT + , UInt32 numThreads + #endif + ) +{ + UInt32 level = _level; + if (oneMethodInfo.MethodName.IsEmpty()) + oneMethodInfo.MethodName = kDefaultMethodName; + + if (IsLZMAMethod(oneMethodInfo.MethodName)) + { + UInt32 dicSize = + (level >= 9 ? kLzmaDicSizeX9 : + (level >= 7 ? kLzmaDicSizeX7 : + (level >= 5 ? kLzmaDicSizeX5 : + (level >= 3 ? kLzmaDicSizeX3 : + kLzmaDicSizeX1)))); + + UInt32 algo = + (level >= 5 ? kLzmaAlgoX5 : + kLzmaAlgoX1); + + UInt32 fastBytes = + (level >= 7 ? kLzmaFastBytesX7 : + kLzmaFastBytesX1); + + const wchar_t *matchFinder = + (level >= 5 ? kLzmaMatchFinderX5 : + kLzmaMatchFinderX1); + + SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kMatchFinder, matchFinder); + #ifdef COMPRESS_MT + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); + #endif + } + else if (IsDeflateMethod(oneMethodInfo.MethodName)) + { + UInt32 fastBytes = + (level >= 9 ? kDeflateFastBytesX9 : + (level >= 7 ? kDeflateFastBytesX7 : + kDeflateFastBytesX1)); + + UInt32 numPasses = + (level >= 9 ? kDeflatePassesX9 : + (level >= 7 ? kDeflatePassesX7 : + kDeflatePassesX1)); + + UInt32 algo = + (level >= 5 ? kDeflateAlgoX5 : + kDeflateAlgoX1); + + SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses); + } + else if (IsBZip2Method(oneMethodInfo.MethodName)) + { + UInt32 numPasses = + (level >= 9 ? kBZip2NumPassesX9 : + (level >= 7 ? kBZip2NumPassesX7 : + kBZip2NumPassesX1)); + + UInt32 dicSize = + (level >= 5 ? kBZip2DicSizeX5 : + (level >= 3 ? kBZip2DicSizeX3 : + kBZip2DicSizeX1)); + + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize); + #ifdef COMPRESS_MT + SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); + #endif + } + else if (IsPpmdMethod(oneMethodInfo.MethodName)) + { + UInt32 useMemSize = + (level >= 9 ? kPpmdMemSizeX9 : + (level >= 7 ? kPpmdMemSizeX7 : + (level >= 5 ? kPpmdMemSizeX5 : + kPpmdMemSizeX1))); + + UInt32 order = + (level >= 9 ? kPpmdOrderX9 : + (level >= 7 ? kPpmdOrderX7 : + (level >= 5 ? kPpmdOrderX5 : + kPpmdOrderX1))); + + SetOneMethodProp(oneMethodInfo, NCoderPropID::kUsedMemorySize, useMemSize); + SetOneMethodProp(oneMethodInfo, NCoderPropID::kOrder, order); + } +} + +static void SplitParams(const UString &srcString, UStringVector &subStrings) +{ + subStrings.Clear(); + UString name; + int len = srcString.Length(); + if (len == 0) + return; + for (int i = 0; i < len; i++) + { + wchar_t c = srcString[i]; + if (c == L':') + { + subStrings.Add(name); + name.Empty(); + } + else + name += c; + } + subStrings.Add(name); +} + +static void SplitParam(const UString ¶m, UString &name, UString &value) +{ + int eqPos = param.Find(L'='); + if (eqPos >= 0) + { + name = param.Left(eqPos); + value = param.Mid(eqPos + 1); + return; + } + for(int i = 0; i < param.Length(); i++) + { + wchar_t c = param[i]; + if (c >= L'0' && c <= L'9') + { + name = param.Left(i); + value = param.Mid(i); + return; + } + } + name = param; +} + +HRESULT COutHandler::SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value) +{ + CProp property; + if ( + name.CompareNoCase(L"D") == 0 || + name.CompareNoCase(L"MEM") == 0) + { + UInt32 dicSize; + RINOK(ParsePropDictionaryValue(value, dicSize)); + if (name.CompareNoCase(L"D") == 0) + property.Id = NCoderPropID::kDictionarySize; + else + property.Id = NCoderPropID::kUsedMemorySize; + property.Value = dicSize; + oneMethodInfo.Properties.Add(property); + } + else + { + int index = FindPropIdFromStringName(name); + if (index < 0) + return E_INVALIDARG; + + const CNameToPropID &nameToPropID = g_NameToPropID[index]; + property.Id = nameToPropID.PropID; + + NCOM::CPropVariant propValue; + + if (nameToPropID.VarType == VT_BSTR) + propValue = value; + else if (nameToPropID.VarType == VT_BOOL) + { + bool res; + if (!StringToBool(value, res)) + return E_INVALIDARG; + propValue = res; + } + else + { + UInt32 number; + if (ParseStringToUInt32(value, number) == value.Length()) + propValue = number; + else + propValue = value; + } + + if (!ConvertProperty(propValue, nameToPropID.VarType, property.Value)) + return E_INVALIDARG; + + oneMethodInfo.Properties.Add(property); + } + return S_OK; +} + +HRESULT COutHandler::SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString) +{ + UStringVector params; + SplitParams(srcString, params); + if (params.Size() > 0) + oneMethodInfo.MethodName = params[0]; + for (int i = 1; i < params.Size(); i++) + { + const UString ¶m = params[i]; + UString name, value; + SplitParam(param, name, value); + RINOK(SetParam(oneMethodInfo, name, value)); + } + return S_OK; +} + +HRESULT COutHandler::SetSolidSettings(const UString &s) +{ + bool res; + if (StringToBool(s, res)) + { + if (res) + InitSolid(); + else + _numSolidFiles = 1; + return S_OK; + } + UString s2 = s; + s2.MakeUpper(); + for (int i = 0; i < s2.Length();) + { + const wchar_t *start = ((const wchar_t *)s2) + i; + const wchar_t *end; + UInt64 v = ConvertStringToUInt64(start, &end); + if (start == end) + { + if (s2[i++] != 'E') + return E_INVALIDARG; + _solidExtension = true; + continue; + } + i += (int)(end - start); + if (i == s2.Length()) + return E_INVALIDARG; + wchar_t c = s2[i++]; + switch(c) + { + case 'F': + if (v < 1) + v = 1; + _numSolidFiles = v; + break; + case 'B': + _numSolidBytes = v; + _numSolidBytesDefined = true; + break; + case 'K': + _numSolidBytes = (v << 10); + _numSolidBytesDefined = true; + break; + case 'M': + _numSolidBytes = (v << 20); + _numSolidBytesDefined = true; + break; + case 'G': + _numSolidBytes = (v << 30); + _numSolidBytesDefined = true; + break; + default: + return E_INVALIDARG; + } + } + return S_OK; +} + +HRESULT COutHandler::SetSolidSettings(const PROPVARIANT &value) +{ + switch(value.vt) + { + case VT_EMPTY: + InitSolid(); + return S_OK; + case VT_BSTR: + return SetSolidSettings(value.bstrVal); + default: + return E_INVALIDARG; + } +} + +void COutHandler::Init() +{ + _removeSfxBlock = false; + _compressHeaders = true; + _encryptHeaders = false; + + WriteModified = true; + WriteCreated = false; + WriteAccessed = false; + + #ifdef COMPRESS_MT + _numThreads = NWindows::NSystem::GetNumberOfProcessors(); + #endif + + _level = 5; + _autoFilter = true; + _volumeMode = false; + _crcSize = 4; + InitSolid(); +} + +void COutHandler::BeforeSetProperty() +{ + Init(); + #ifdef COMPRESS_MT + numProcessors = NSystem::GetNumberOfProcessors(); + #endif + + mainDicSize = 0xFFFFFFFF; + mainDicMethodIndex = 0xFFFFFFFF; + minNumber = 0; + _crcSize = 4; +} + +HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) +{ + UString name = nameSpec; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + + if (name[0] == 'X') + { + name.Delete(0); + _level = 9; + return ParsePropValue(name, value, _level); + } + + if (name[0] == L'S') + { + name.Delete(0); + if (name.IsEmpty()) + return SetSolidSettings(value); + if (value.vt != VT_EMPTY) + return E_INVALIDARG; + return SetSolidSettings(name); + } + + if (name == L"CRC") + { + _crcSize = 4; + name.Delete(0, 3); + return ParsePropValue(name, value, _crcSize); + } + + UInt32 number; + int index = ParseStringToUInt32(name, number); + UString realName = name.Mid(index); + if (index == 0) + { + if(name.Left(2).CompareNoCase(L"MT") == 0) + { + #ifdef COMPRESS_MT + RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads)); + #endif + return S_OK; + } + if (name.CompareNoCase(L"RSFX") == 0) + return SetBoolProperty(_removeSfxBlock, value); + if (name.CompareNoCase(L"F") == 0) + return SetBoolProperty(_autoFilter, value); + if (name.CompareNoCase(L"HC") == 0) + return SetBoolProperty(_compressHeaders, value); + if (name.CompareNoCase(L"HCF") == 0) + { + bool compressHeadersFull = true; + RINOK(SetBoolProperty(compressHeadersFull, value)); + if (!compressHeadersFull) + return E_INVALIDARG; + return S_OK; + } + if (name.CompareNoCase(L"HE") == 0) + return SetBoolProperty(_encryptHeaders, value); + if (name.CompareNoCase(L"TM") == 0) + return SetBoolProperty(WriteModified, value); + if (name.CompareNoCase(L"TC") == 0) + return SetBoolProperty(WriteCreated, value); + if (name.CompareNoCase(L"TA") == 0) + return SetBoolProperty(WriteAccessed, value); + if (name.CompareNoCase(L"V") == 0) + return SetBoolProperty(_volumeMode, value); + number = 0; + } + if (number > 10000) + return E_FAIL; + if (number < minNumber) + return E_INVALIDARG; + number -= minNumber; + for(int j = _methods.Size(); j <= (int)number; j++) + { + COneMethodInfo oneMethodInfo; + _methods.Add(oneMethodInfo); + } + + COneMethodInfo &oneMethodInfo = _methods[number]; + + if (realName.Length() == 0) + { + if (value.vt != VT_BSTR) + return E_INVALIDARG; + + RINOK(SetParams(oneMethodInfo, value.bstrVal)); + } + else + { + CProp property; + if (realName.Left(1).CompareNoCase(L"D") == 0) + { + UInt32 dicSize; + RINOK(ParsePropDictionaryValue(realName.Mid(1), value, dicSize)); + property.Id = NCoderPropID::kDictionarySize; + property.Value = dicSize; + oneMethodInfo.Properties.Add(property); + if (number <= mainDicMethodIndex) + mainDicSize = dicSize; + } + else if (realName.Left(3).CompareNoCase(L"MEM") == 0) + { + UInt32 dicSize; + RINOK(ParsePropDictionaryValue(realName.Mid(3), value, dicSize)); + property.Id = NCoderPropID::kUsedMemorySize; + property.Value = dicSize; + oneMethodInfo.Properties.Add(property); + if (number <= mainDicMethodIndex) + mainDicSize = dicSize; + } + else + { + int index = FindPropIdFromStringName(realName); + if (index < 0) + return E_INVALIDARG; + + const CNameToPropID &nameToPropID = g_NameToPropID[index]; + property.Id = nameToPropID.PropID; + + if (!ConvertProperty(value, nameToPropID.VarType, property.Value)) + return E_INVALIDARG; + + oneMethodInfo.Properties.Add(property); + } + } + return S_OK; +} + +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.h new file mode 100644 index 0000000..ab925cc --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/HandlerOut.h @@ -0,0 +1,86 @@ +// HandlerOut.h + +#ifndef __HANDLER_OUT_H +#define __HANDLER_OUT_H + +#include "../../Common/MethodProps.h" +#include "../../Common/CreateCoder.h" + +namespace NArchive { + +struct COneMethodInfo +{ + CObjectVector Properties; + UString MethodName; +}; + +class COutHandler +{ +public: + HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); + + HRESULT SetSolidSettings(const UString &s); + HRESULT SetSolidSettings(const PROPVARIANT &value); + + #ifdef COMPRESS_MT + UInt32 _numThreads; + #endif + + UInt32 _crcSize; + + CObjectVector _methods; + bool _removeSfxBlock; + + UInt64 _numSolidFiles; + UInt64 _numSolidBytes; + bool _numSolidBytesDefined; + bool _solidExtension; + + bool _compressHeaders; + bool _encryptHeaders; + + bool WriteModified; + bool WriteCreated; + bool WriteAccessed; + + bool _autoFilter; + UInt32 _level; + + bool _volumeMode; + + HRESULT SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value); + HRESULT SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString); + + void SetCompressionMethod2(COneMethodInfo &oneMethodInfo + #ifdef COMPRESS_MT + , UInt32 numThreads + #endif + ); + + void InitSolidFiles() { _numSolidFiles = (UInt64)(Int64)(-1); } + void InitSolidSize() { _numSolidBytes = (UInt64)(Int64)(-1); } + void InitSolid() + { + InitSolidFiles(); + InitSolidSize(); + _solidExtension = false; + _numSolidBytesDefined = false; + } + + void Init(); + + COutHandler() { Init(); } + + void BeforeSetProperty(); + + UInt32 minNumber; + UInt32 numProcessors; + UInt32 mainDicSize; + UInt32 mainDicMethodIndex; + + DECL_EXTERNAL_CODECS_VARS +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp new file mode 100644 index 0000000..1d9e555 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp @@ -0,0 +1,40 @@ +// InStreamWithCRC.cpp + +#include "StdAfx.h" + +#include "InStreamWithCRC.h" + +STDMETHODIMP CSequentialInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + if (size > 0 && realProcessedSize == 0) + _wasFinished = true; + _crc = CrcUpdate(_crc, data, realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + if (size > 0 && realProcessedSize == 0) + _wasFinished = true; + _size += realProcessedSize; + _crc = CrcUpdate(_crc, data, realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CInStreamWithCRC::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if (seekOrigin != STREAM_SEEK_SET || offset != 0) + return E_FAIL; + _size = 0; + _crc = CRC_INIT_VAL; + return _stream->Seek(offset, seekOrigin, newPosition); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h new file mode 100644 index 0000000..96bea9b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h @@ -0,0 +1,69 @@ +// InStreamWithCRC.h + +#ifndef __INSTREAMWITHCRC_H +#define __INSTREAMWITHCRC_H + +#include "../../../Common/MyCom.h" +#include "../../IStream.h" + +extern "C" +{ +#include "../../../../C/7zCrc.h" +} + +class CSequentialInStreamWithCRC: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +private: + CMyComPtr _stream; + UInt64 _size; + UInt32 _crc; + bool _wasFinished; +public: + void SetStream(ISequentialInStream *stream) { _stream = stream; } + void Init() + { + _size = 0; + _wasFinished = false; + _crc = CRC_INIT_VAL; + } + void ReleaseStream() { _stream.Release(); } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } + UInt64 GetSize() const { return _size; } + bool WasFinished() const { return _wasFinished; } +}; + +class CInStreamWithCRC: + public IInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +private: + CMyComPtr _stream; + UInt64 _size; + UInt32 _crc; + bool _wasFinished; +public: + void SetStream(IInStream *stream) { _stream = stream; } + void Init() + { + _size = 0; + _wasFinished = false; + _crc = CRC_INIT_VAL; + } + void ReleaseStream() { _stream.Release(); } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } + UInt64 GetSize() const { return _size; } + bool WasFinished() const { return _wasFinished; } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp new file mode 100644 index 0000000..f7c3fcd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp @@ -0,0 +1,59 @@ +// Archive/Common/ItemNameUtils.cpp + +#include "StdAfx.h" + +#include "ItemNameUtils.h" + +namespace NArchive { +namespace NItemName { + +static const wchar_t kOSDirDelimiter = WCHAR_PATH_SEPARATOR; +static const wchar_t kDirDelimiter = L'/'; + +UString MakeLegalName(const UString &name) +{ + UString zipName = name; + zipName.Replace(kOSDirDelimiter, kDirDelimiter); + return zipName; +} + +UString GetOSName(const UString &name) +{ + UString newName = name; + newName.Replace(kDirDelimiter, kOSDirDelimiter); + return newName; +} + +UString GetOSName2(const UString &name) +{ + if (name.IsEmpty()) + return UString(); + UString newName = GetOSName(name); + if (newName[newName.Length() - 1] == kOSDirDelimiter) + newName.Delete(newName.Length() - 1); + return newName; +} + +bool HasTailSlash(const AString &name, UINT codePage) +{ + if (name.IsEmpty()) + return false; + LPCSTR prev = + #ifdef _WIN32 + CharPrevExA((WORD)codePage, name, &name[name.Length()], 0); + #else + (LPCSTR)(name) + (name.Length() - 1); + #endif + return (*prev == '/'); +} + +#ifndef _WIN32 +UString WinNameToOSName(const UString &name) +{ + UString newName = name; + newName.Replace(L'\\', kOSDirDelimiter); + return newName; +} +#endif + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h new file mode 100644 index 0000000..5eafacb --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h @@ -0,0 +1,24 @@ +// Archive/Common/ItemNameUtils.h + +#ifndef __ARCHIVE_ITEMNAMEUTILS_H +#define __ARCHIVE_ITEMNAMEUTILS_H + +#include "../../../Common/MyString.h" + +namespace NArchive { +namespace NItemName { + + UString MakeLegalName(const UString &name); + UString GetOSName(const UString &name); + UString GetOSName2(const UString &name); + bool HasTailSlash(const AString &name, UINT codePage); + + #ifdef _WIN32 + inline UString WinNameToOSName(const UString &name) { return name; } + #else + UString WinNameToOSName(const UString &name); + #endif + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.cpp new file mode 100644 index 0000000..a8cb333 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.cpp @@ -0,0 +1,201 @@ +// MultiStream.cpp + +#include "StdAfx.h" + +#include "MultiStream.h" + +STDMETHODIMP CMultiStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + while(_streamIndex < Streams.Size() && size > 0) + { + CSubStreamInfo &s = Streams[_streamIndex]; + if (_pos == s.Size) + { + _streamIndex++; + _pos = 0; + continue; + } + RINOK(s.Stream->Seek(s.Pos + _pos, STREAM_SEEK_SET, 0)); + UInt32 sizeToRead = UInt32(MyMin((UInt64)size, s.Size - _pos)); + UInt32 realProcessed; + HRESULT result = s.Stream->Read(data, sizeToRead, &realProcessed); + data = (void *)((Byte *)data + realProcessed); + size -= realProcessed; + if(processedSize != NULL) + *processedSize += realProcessed; + _pos += realProcessed; + _seekPos += realProcessed; + RINOK(result); + break; + } + return S_OK; +} + +STDMETHODIMP CMultiStream::Seek(Int64 offset, UInt32 seekOrigin, + UInt64 *newPosition) +{ + UInt64 newPos; + switch(seekOrigin) + { + case STREAM_SEEK_SET: + newPos = offset; + break; + case STREAM_SEEK_CUR: + newPos = _seekPos + offset; + break; + case STREAM_SEEK_END: + newPos = _totalLength + offset; + break; + default: + return STG_E_INVALIDFUNCTION; + } + _seekPos = 0; + for (_streamIndex = 0; _streamIndex < Streams.Size(); _streamIndex++) + { + UInt64 size = Streams[_streamIndex].Size; + if (newPos < _seekPos + size) + { + _pos = newPos - _seekPos; + _seekPos += _pos; + if (newPosition != 0) + *newPosition = newPos; + return S_OK; + } + _seekPos += size; + } + if (newPos == _seekPos) + { + if (newPosition != 0) + *newPosition = newPos; + return S_OK; + } + return E_FAIL; +} + + +/* +class COutVolumeStream: + public ISequentialOutStream, + public CMyUnknownImp +{ + int _volIndex; + UInt64 _volSize; + UInt64 _curPos; + CMyComPtr _volumeStream; + COutArchive _archive; + CCRC _crc; + +public: + MY_UNKNOWN_IMP + + CFileItem _file; + CUpdateOptions _options; + CMyComPtr VolumeCallback; + void Init(IArchiveUpdateCallback2 *volumeCallback, + const UString &name) + { + _file.Name = name; + _file.IsStartPosDefined = true; + _file.StartPos = 0; + + VolumeCallback = volumeCallback; + _volIndex = 0; + _volSize = 0; + } + + HRESULT Flush(); + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +HRESULT COutVolumeStream::Flush() +{ + if (_volumeStream) + { + _file.UnPackSize = _curPos; + _file.FileCRC = _crc.GetDigest(); + RINOK(WriteVolumeHeader(_archive, _file, _options)); + _archive.Close(); + _volumeStream.Release(); + _file.StartPos += _file.UnPackSize; + } + return S_OK; +} +*/ + +/* +STDMETHODIMP COutMultiStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + while(size > 0) + { + if (_streamIndex >= Streams.Size()) + { + CSubStreamInfo subStream; + RINOK(VolumeCallback->GetVolumeSize(Streams.Size(), &subStream.Size)); + RINOK(VolumeCallback->GetVolumeStream(Streams.Size(), &subStream.Stream)); + subStream.Pos = 0; + Streams.Add(subStream); + continue; + } + CSubStreamInfo &subStream = Streams[_streamIndex]; + if (_offsetPos >= subStream.Size) + { + _offsetPos -= subStream.Size; + _streamIndex++; + continue; + } + if (_offsetPos != subStream.Pos) + { + CMyComPtr outStream; + RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); + RINOK(outStream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); + subStream.Pos = _offsetPos; + } + + UInt32 curSize = (UInt32)MyMin((UInt64)size, subStream.Size - subStream.Pos); + UInt32 realProcessed; + RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); + data = (void *)((Byte *)data + realProcessed); + size -= realProcessed; + subStream.Pos += realProcessed; + _offsetPos += realProcessed; + _absPos += realProcessed; + if (_absPos > _length) + _length = _absPos; + if(processedSize != NULL) + *processedSize += realProcessed; + if (subStream.Pos == subStream.Size) + { + _streamIndex++; + _offsetPos = 0; + } + if (realProcessed != curSize && realProcessed == 0) + return E_FAIL; + } + return S_OK; +} + +STDMETHODIMP COutMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if(seekOrigin >= 3) + return STG_E_INVALIDFUNCTION; + switch(seekOrigin) + { + case STREAM_SEEK_SET: + _absPos = offset; + break; + case STREAM_SEEK_CUR: + _absPos += offset; + break; + case STREAM_SEEK_END: + _absPos = _length + offset; + break; + } + _offsetPos = _absPos; + _streamIndex = 0; + return S_OK; +} +*/ diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.h new file mode 100644 index 0000000..b0fe41d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/MultiStream.h @@ -0,0 +1,76 @@ +// MultiStream.h + +#ifndef __MULTISTREAM_H +#define __MULTISTREAM_H + +#include "../../../Common/MyCom.h" +#include "../../../Common/MyVector.h" +#include "../../Archive/IArchive.h" + +class CMultiStream: + public IInStream, + public CMyUnknownImp +{ + int _streamIndex; + UInt64 _pos; + UInt64 _seekPos; + UInt64 _totalLength; +public: + struct CSubStreamInfo + { + CMyComPtr Stream; + UInt64 Pos; + UInt64 Size; + }; + CObjectVector Streams; + void Init() + { + _streamIndex = 0; + _pos = 0; + _seekPos = 0; + _totalLength = 0; + for (int i = 0; i < Streams.Size(); i++) + _totalLength += Streams[i].Size; + } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +/* +class COutMultiStream: + public IOutStream, + public CMyUnknownImp +{ + int _streamIndex; // required stream + UInt64 _offsetPos; // offset from start of _streamIndex index + UInt64 _absPos; + UInt64 _length; + + struct CSubStreamInfo + { + CMyComPtr Stream; + UInt64 Size; + UInt64 Pos; + }; + CObjectVector Streams; +public: + CMyComPtr VolumeCallback; + void Init() + { + _streamIndex = 0; + _offsetPos = 0; + _absPos = 0; + _length = 0; + } + + MY_UNKNOWN_IMP1(IOutStream) + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; +*/ + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp new file mode 100644 index 0000000..2ab2da6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp @@ -0,0 +1,24 @@ +// OutStreamWithCRC.cpp + +#include "StdAfx.h" + +#include "OutStreamWithCRC.h" + +STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result; + if(!_stream) + { + realProcessedSize = size; + result = S_OK; + } + else + result = _stream->Write(data, size, &realProcessedSize); + if (_calculate) + _crc = CrcUpdate(_crc, data, realProcessedSize); + _size += realProcessedSize; + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h new file mode 100644 index 0000000..eaeecde --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h @@ -0,0 +1,38 @@ +// OutStreamWithCRC.h + +#ifndef __OUTSTREAMWITHCRC_H +#define __OUTSTREAMWITHCRC_H + +#include "../../../Common/MyCom.h" +#include "../../IStream.h" + +extern "C" +{ +#include "../../../../C/7zCrc.h" +} + +class COutStreamWithCRC: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr _stream; + UInt64 _size; + UInt32 _crc; + bool _calculate; +public: + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void ReleaseStream() { _stream.Release(); } + void Init(bool calculate = true) + { + _size = 0; + _calculate = calculate; + _crc = CRC_INIT_VAL; + } + void InitCRC() { _crc = CRC_INIT_VAL; } + UInt64 GetSize() const { return _size; } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp new file mode 100644 index 0000000..f0d4e29 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp @@ -0,0 +1,174 @@ +// ParseProperties.cpp + +#include "StdAfx.h" + +#include "ParseProperties.h" + +#include "Common/StringToInt.h" +#include "Common/MyCom.h" + +HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) +{ + if (prop.vt == VT_UI4) + { + if (!name.IsEmpty()) + return E_INVALIDARG; + resValue = prop.ulVal; + } + else if (prop.vt == VT_EMPTY) + { + if(!name.IsEmpty()) + { + const wchar_t *start = name; + const wchar_t *end; + UInt64 v = ConvertStringToUInt64(start, &end); + if (end - start != name.Length()) + return E_INVALIDARG; + resValue = (UInt32)v; + } + } + else + return E_INVALIDARG; + return S_OK; +} + +static const int kLogarithmicSizeLimit = 32; +static const wchar_t kByteSymbol = L'B'; +static const wchar_t kKiloByteSymbol = L'K'; +static const wchar_t kMegaByteSymbol = L'M'; + +HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize) +{ + UString srcString = srcStringSpec; + srcString.MakeUpper(); + + const wchar_t *start = srcString; + const wchar_t *end; + UInt64 number = ConvertStringToUInt64(start, &end); + int numDigits = (int)(end - start); + if (numDigits == 0 || srcString.Length() > numDigits + 1) + return E_INVALIDARG; + if (srcString.Length() == numDigits) + { + if (number >= kLogarithmicSizeLimit) + return E_INVALIDARG; + dicSize = (UInt32)1 << (int)number; + return S_OK; + } + switch (srcString[numDigits]) + { + case kByteSymbol: + if (number >= ((UInt64)1 << kLogarithmicSizeLimit)) + return E_INVALIDARG; + dicSize = (UInt32)number; + break; + case kKiloByteSymbol: + if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 10))) + return E_INVALIDARG; + dicSize = (UInt32)(number << 10); + break; + case kMegaByteSymbol: + if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 20))) + return E_INVALIDARG; + dicSize = (UInt32)(number << 20); + break; + default: + return E_INVALIDARG; + } + return S_OK; +} + +HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) +{ + if (name.IsEmpty()) + { + if (prop.vt == VT_UI4) + { + UInt32 logDicSize = prop.ulVal; + if (logDicSize >= 32) + return E_INVALIDARG; + resValue = (UInt32)1 << logDicSize; + return S_OK; + } + if (prop.vt == VT_BSTR) + return ParsePropDictionaryValue(prop.bstrVal, resValue); + return E_INVALIDARG; + } + return ParsePropDictionaryValue(name, resValue); +} + +bool StringToBool(const UString &s, bool &res) +{ + if (s.IsEmpty() || s.CompareNoCase(L"ON") == 0) + { + res = true; + return true; + } + if (s.CompareNoCase(L"OFF") == 0) + { + res = false; + return true; + } + return false; +} + +HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value) +{ + switch(value.vt) + { + case VT_EMPTY: + dest = true; + return S_OK; + /* + case VT_UI4: + dest = (value.ulVal != 0); + break; + */ + case VT_BSTR: + return StringToBool(value.bstrVal, dest) ? S_OK : E_INVALIDARG; + } + return E_INVALIDARG; +} + +int ParseStringToUInt32(const UString &srcString, UInt32 &number) +{ + const wchar_t *start = srcString; + const wchar_t *end; + UInt64 number64 = ConvertStringToUInt64(start, &end); + if (number64 > 0xFFFFFFFF) + { + number = 0; + return 0; + } + number = (UInt32)number64; + return (int)(end - start); +} + +HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads) +{ + if (name.IsEmpty()) + { + switch(prop.vt) + { + case VT_UI4: + numThreads = prop.ulVal; + break; + default: + { + bool val; + RINOK(SetBoolProperty(val, prop)); + numThreads = (val ? defaultNumThreads : 1); + break; + } + } + } + else + { + UInt32 number; + int index = ParseStringToUInt32(name, number); + if (index != name.Length()) + return E_INVALIDARG; + numThreads = number; + } + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.h new file mode 100644 index 0000000..6f80f63 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/Common/ParseProperties.h @@ -0,0 +1,18 @@ +// ParseProperties.h + +#ifndef __PARSEPROPERTIES_H +#define __PARSEPROPERTIES_H + +#include "Common/MyString.h" +#include "Common/Types.h" + +HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); +HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize); +HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); + +bool StringToBool(const UString &s, bool &res); +HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value); +int ParseStringToUInt32(const UString &srcString, UInt32 &number); +HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/DllExports2.cpp b/3rdparty/physfs/lzma/CPP/7zip/Archive/DllExports2.cpp new file mode 100644 index 0000000..d3b15f0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/DllExports2.cpp @@ -0,0 +1,82 @@ +// DLLExports.cpp + +#include "StdAfx.h" + +#include "../../Common/MyInitGuid.h" +#include "../../Common/ComTry.h" +#include "../../Common/Types.h" +#include "../../Windows/PropVariant.h" +#if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES) +extern "C" +{ +#include "../../../C/Alloc.h" +} +#endif + +#include "IArchive.h" +#include "../ICoder.h" +#include "../IPassword.h" + +HINSTANCE g_hInstance; +#ifndef _UNICODE +#ifdef _WIN32 +bool g_IsNT = false; +static bool IsItWindowsNT() +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + if (!::GetVersionEx(&versionInfo)) + return false; + return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); +} +#endif +#endif + +extern "C" +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + g_hInstance = hInstance; + #ifndef _UNICODE + #ifdef _WIN32 + g_IsNT = IsItWindowsNT(); + #endif + #endif + } + return TRUE; +} + +DEFINE_GUID(CLSID_CArchiveHandler, +0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00); + +static const UInt16 kDecodeId = 0x2790; + +DEFINE_GUID(CLSID_CCodec, +0x23170F69, 0x40C1, kDecodeId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +STDAPI CreateCoder(const GUID *clsid, const GUID *iid, void **outObject); +STDAPI CreateArchiver(const GUID *classID, const GUID *iid, void **outObject); + +STDAPI CreateObject(const GUID *clsid, const GUID *iid, void **outObject) +{ + // COM_TRY_BEGIN + *outObject = 0; + if (*iid == IID_ICompressCoder || *iid == IID_ICompressCoder2 || *iid == IID_ICompressFilter) + { + return CreateCoder(clsid, iid, outObject); + } + else + { + return CreateArchiver(clsid, iid, outObject); + } + // COM_TRY_END +} + +STDAPI SetLargePageMode() +{ + #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES) + SetLargePageSize(); + #endif + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Archive/IArchive.h b/3rdparty/physfs/lzma/CPP/7zip/Archive/IArchive.h new file mode 100644 index 0000000..e0ae7aa --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Archive/IArchive.h @@ -0,0 +1,207 @@ +// IArchive.h + +#ifndef __IARCHIVE_H +#define __IARCHIVE_H + +#include "../IStream.h" +#include "../IProgress.h" +#include "../PropID.h" + +#define ARCHIVE_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 6, x) +#define ARCHIVE_INTERFACE(i, x) ARCHIVE_INTERFACE_SUB(i, IUnknown, x) + +namespace NFileTimeType +{ + enum EEnum + { + kWindows, + kUnix, + kDOS + }; +} + +namespace NArchive +{ + enum + { + kName = 0, + kClassID, + kExtension, + kAddExtension, + kUpdate, + kKeepName, + kStartSignature, + kFinishSignature, + kAssociate + }; + + namespace NExtract + { + namespace NAskMode + { + enum + { + kExtract = 0, + kTest, + kSkip + }; + } + namespace NOperationResult + { + enum + { + kOK = 0, + kUnSupportedMethod, + kDataError, + kCRCError + }; + } + } + namespace NUpdate + { + namespace NOperationResult + { + enum + { + kOK = 0, + kError + }; + } + } +} + +ARCHIVE_INTERFACE(IArchiveOpenCallback, 0x10) +{ + STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes) PURE; + STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes) PURE; +}; + + +ARCHIVE_INTERFACE_SUB(IArchiveExtractCallback, IProgress, 0x20) +{ + STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, + Int32 askExtractMode) PURE; + // GetStream OUT: S_OK - OK, S_FALSE - skeep this file + STDMETHOD(PrepareOperation)(Int32 askExtractMode) PURE; + STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) PURE; +}; + + +ARCHIVE_INTERFACE(IArchiveOpenVolumeCallback, 0x30) +{ + STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) PURE; + STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) PURE; +}; + + +ARCHIVE_INTERFACE(IInArchiveGetStream, 0x40) +{ + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) PURE; +}; + + +ARCHIVE_INTERFACE(IArchiveOpenSetSubArchiveName, 0x50) +{ + STDMETHOD(SetSubArchiveName)(const wchar_t *name) PURE; +}; + + +/* +IInArchive::Extract: + indices must be sorted + numItems = 0xFFFFFFFF means "all files" + testMode != 0 means "test files without writing to outStream" +*/ + +#define INTERFACE_IInArchive(x) \ + STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) x; \ + STDMETHOD(Close)() x; \ + STDMETHOD(GetNumberOfItems)(UInt32 *numItems) x; \ + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) x; \ + STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties) x; \ + STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x; \ + STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties) x; \ + STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x; + +ARCHIVE_INTERFACE(IInArchive, 0x60) +{ + INTERFACE_IInArchive(PURE) +}; + + +ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback, IProgress, 0x80) +{ + STDMETHOD(GetUpdateItemInfo)(UInt32 index, + Int32 *newData, // 1 - new data, 0 - old data + Int32 *newProperties, // 1 - new properties, 0 - old properties + UInt32 *indexInArchive // -1 if there is no in archive, or if doesn't matter + ) PURE; + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream) PURE; + STDMETHOD(SetOperationResult)(Int32 operationResult) PURE; +}; + + +ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82) +{ + STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size) PURE; + STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream) PURE; +}; + + +#define INTERFACE_IOutArchive(x) \ + STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) x; \ + STDMETHOD(GetFileTimeType)(UInt32 *type) x; + +ARCHIVE_INTERFACE(IOutArchive, 0xA0) +{ + INTERFACE_IOutArchive(PURE) +}; + + +ARCHIVE_INTERFACE(ISetProperties, 0x03) +{ + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) PURE; +}; + + +#define IMP_IInArchive_GetProp(k) \ + (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ + { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \ + const STATPROPSTG &srcItem = k[index]; \ + *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; } \ + +#define IMP_IInArchive_GetProp_WITH_NAME(k) \ + (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ + { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \ + const STATPROPSTG &srcItem = k[index]; \ + *propID = srcItem.propid; *varType = srcItem.vt; \ + if (srcItem.lpwstrName == 0) *name = 0; else *name = ::SysAllocString(srcItem.lpwstrName); return S_OK; } \ + +#define IMP_IInArchive_Props \ + STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp(kProps) + +#define IMP_IInArchive_Props_WITH_NAME \ + STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kProps) + + +#define IMP_IInArchive_ArcProps \ + STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kArcProps) / sizeof(kArcProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp(kArcProps) + +#define IMP_IInArchive_ArcProps_NO \ + STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \ + { *numProperties = 0; return S_OK; } \ + STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *) \ + { return E_NOTIMPL; } \ + STDMETHODIMP CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value) \ + { value->vt = VT_EMPTY; return S_OK; } + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp new file mode 100644 index 0000000..b9f389b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp @@ -0,0 +1,1453 @@ +# Microsoft Developer Studio Project File - Name="Alone" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Alone - Win32 DebugU +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Alone.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Alone.mak" CFG="Alone - Win32 DebugU" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Alone - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Alone - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "Alone - Win32 ReleaseU" (based on "Win32 (x86) Console Application") +!MESSAGE "Alone - Win32 DebugU" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Alone - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /Gz /MT /W3 /GX /O1 /I "..\..\..\\" /D "NDEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7zr.exe" /opt:NOWIN98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /Gz /MDd /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7zr.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ReleaseU" +# PROP BASE Intermediate_Dir "ReleaseU" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "ReleaseU" +# PROP Intermediate_Dir "ReleaseU" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /Yu"StdAfx.h" /FD /c +# ADD CPP /nologo /Gz /MD /W3 /GX /O1 /I "..\..\..\\" /D "NDEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7za.exe" /opt:NOWIN98 +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7zr.exe" /opt:NOWIN98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "DebugU" +# PROP BASE Intermediate_Dir "DebugU" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugU" +# PROP Intermediate_Dir "DebugU" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /D "_MBCS" /Yu"StdAfx.h" /FD /GZ /c +# ADD CPP /nologo /Gz /W4 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7za.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7zr.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Alone - Win32 Release" +# Name "Alone - Win32 Debug" +# Name "Alone - Win32 ReleaseU" +# Name "Alone - Win32 DebugU" +# Begin Group "Console" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\UI\Console\ArError.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\CompressionMode.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\ConsoleClose.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\ConsoleClose.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\ExtractCallbackConsole.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\ExtractCallbackConsole.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\List.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\List.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\Main.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\MainAr.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\OpenCallbackConsole.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\OpenCallbackConsole.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\PercentPrinter.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\PercentPrinter.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\UpdateCallbackConsole.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\UpdateCallbackConsole.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\UserInputUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Console\UserInputUtils.h +# End Source File +# End Group +# Begin Group "Spec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\resource.rc +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"StdAfx.h" +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Common\AutoPtr.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Buffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\CommandLineParser.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\CommandLineParser.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\ComTry.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\CRC.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Defs.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\DynamicBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\ListFileUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\ListFileUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyCom.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyException.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyGuidDef.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyInitGuid.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyUnknown.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyWindows.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyWindows.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Random.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Random.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StdInStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StdInStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StdOutStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StdOutStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringToInt.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringToInt.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Types.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\UTFConvert.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\UTFConvert.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Wildcard.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Wildcard.h +# End Source File +# End Group +# Begin Group "Windows" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Windows\Defs.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Device.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\DLL.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\DLL.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Error.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Error.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileDir.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileDir.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileFind.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileFind.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileName.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileName.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Handle.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\MemoryLock.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\MemoryLock.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariant.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariant.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariantConversions.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariantConversions.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Synchronization.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Synchronization.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\System.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\System.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Thread.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Time.h +# End Source File +# End Group +# Begin Group "7zip Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Common\CreateCoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\CreateCoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\CrossThreadProgress.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\CrossThreadProgress.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FilePathAutoRename.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FilePathAutoRename.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FileStreams.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FileStreams.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FilterCoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FilterCoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InOutTempBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InOutTempBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LimitedStreams.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LimitedStreams.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LockedStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LockedStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LSBFDecoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LSBFDecoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LSBFEncoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\LSBFEncoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MethodId.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MethodId.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MethodProps.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MethodProps.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MSBFDecoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\MSBFEncoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OffsetStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OffsetStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OutBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OutBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\ProgressMt.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\ProgressMt.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\ProgressUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\ProgressUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\RegisterArc.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\RegisterCodec.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamBinder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamBinder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamObjects.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamObjects.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\VirtThread.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\VirtThread.h +# End Source File +# End Group +# Begin Group "Compress" + +# PROP Default_Filter "" +# Begin Group "Branch" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchCoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\BranchCoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\Coder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\x86.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\x86.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\x86_2.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Branch\x86_2.h +# End Source File +# End Group +# Begin Group "Copy" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\Copy\CopyCoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Copy\CopyCoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\Copy\CopyRegister.cpp +# End Source File +# End Group +# Begin Group "LZ" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\LZ\LZOutWindow.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZ\LZOutWindow.h +# End Source File +# End Group +# Begin Group "LZMA" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMA.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMADecoder.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMADecoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMAEncoder.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O2 +# SUBTRACT CPP /YX /Yc /Yu + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMAEncoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA\LZMARegister.cpp +# End Source File +# End Group +# Begin Group "RangeCoder" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\RangeCoder\RangeCoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\RangeCoder\RangeCoderBit.cpp + +!IF "$(CFG)" == "Alone - Win32 Release" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "Alone - Win32 Debug" + +!ELSEIF "$(CFG)" == "Alone - Win32 ReleaseU" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "Alone - Win32 DebugU" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\RangeCoder\RangeCoderBit.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\RangeCoder\RangeCoderBitTree.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\RangeCoder\RangeCoderOpt.h +# End Source File +# End Group +# Begin Group "LZMA_Alone" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Compress\LZMA_Alone\LzmaBench.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA_Alone\LzmaBench.h +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA_Alone\LzmaBenchCon.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Compress\LZMA_Alone\LzmaBenchCon.h +# End Source File +# End Group +# End Group +# Begin Group "Archive" + +# PROP Default_Filter "" +# Begin Group "7z" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Archive\7z\7zCompressionMode.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zCompressionMode.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zDecode.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zDecode.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zEncode.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zEncode.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zExtract.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zFolderInStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zFolderInStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zFolderOutStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zFolderOutStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zHandler.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zHandlerOut.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zHeader.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zHeader.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zIn.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zIn.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zItem.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zOut.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zOut.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zProperties.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zProperties.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zRegister.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zSpecStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zSpecStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zUpdate.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\7z\7zUpdate.h +# End Source File +# End Group +# Begin Group "Archive Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Archive\Common\CoderMixer2.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\CoderMixer2.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\CoderMixer2MT.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\CoderMixer2MT.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\DummyOutStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\DummyOutStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\HandlerOut.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\HandlerOut.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\InStreamWithCRC.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\InStreamWithCRC.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\ItemNameUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\ItemNameUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\MultiStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\MultiStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\OutStreamWithCRC.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\OutStreamWithCRC.h +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\ParseProperties.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Common\ParseProperties.h +# End Source File +# End Group +# Begin Group "split" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Archive\Split\SplitHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Archive\Split\SplitHandler.h +# End Source File +# End Group +# End Group +# Begin Group "UI Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveCommandLine.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveCommandLine.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveExtractCallback.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveExtractCallback.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveOpenCallback.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ArchiveOpenCallback.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\DefaultName.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\DefaultName.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\EnumDirItems.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\EnumDirItems.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\Extract.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\Extract.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ExtractingFilePath.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\ExtractingFilePath.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\LoadCodecs.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\LoadCodecs.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\OpenArchive.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\OpenArchive.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\Property.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\PropIDUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\PropIDUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\SetProperties.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\SetProperties.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\SortUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\SortUtils.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\TempFiles.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\TempFiles.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\Update.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\Update.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateAction.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateAction.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateCallback.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateCallback.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdatePair.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdatePair.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateProduce.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\UpdateProduce.h +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\WorkDir.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\UI\Common\WorkDir.h +# End Source File +# End Group +# Begin Group "7-zip" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\ICoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\IMyUnknown.h +# End Source File +# Begin Source File + +SOURCE=..\..\IPassword.h +# End Source File +# Begin Source File + +SOURCE=..\..\IProgress.h +# End Source File +# Begin Source File + +SOURCE=..\..\IStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\PropID.h +# End Source File +# End Group +# Begin Group "C" + +# PROP Default_Filter "" +# Begin Group "C Branch" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchARM.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchARM.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchARMThumb.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchARMThumb.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchIA64.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchIA64.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchPPC.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchPPC.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchSPARC.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchSPARC.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchTypes.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchX86.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchX86.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\..\C\7zCrc.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\7zCrc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Alloc.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Alloc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\IStream.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\LzHash.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinder.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinder.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinderMt.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinderMt.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Threads.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Threads.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Types.h +# End Source File +# End Group +# End Target +# End Project diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw new file mode 100644 index 0000000..65eca43 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Alone"=.\Alone.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h new file mode 100644 index 0000000..2e4be10 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" +#include "../../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/makefile b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/makefile new file mode 100644 index 0000000..1feb1fb --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/makefile @@ -0,0 +1,236 @@ +PROG = 7za.exe +LIBS = $(LIBS) user32.lib oleaut32.lib Advapi32.lib + +CFLAGS = $(CFLAGS) -I ../../../ \ + -D_NO_CRYPTO \ + -DWIN_LONG_PATH \ + -DCOMPRESS_MT \ + -DCOMPRESS_MF_MT \ + -D_NO_CRYPTO \ + -DBREAK_HANDLER \ + -DBENCH_MT \ + + +CONSOLE_OBJS = \ + $O\ConsoleClose.obj \ + $O\ExtractCallbackConsole.obj \ + $O\List.obj \ + $O\Main.obj \ + $O\MainAr.obj \ + $O\OpenCallbackConsole.obj \ + $O\PercentPrinter.obj \ + $O\UpdateCallbackConsole.obj \ + $O\UserInputUtils.obj \ + +COMMON_OBJS = \ + $O\CommandLineParser.obj \ + $O\CRC.obj \ + $O\IntToString.obj \ + $O\ListFileUtils.obj \ + $O\NewHandler.obj \ + $O\StdInStream.obj \ + $O\StdOutStream.obj \ + $O\MyString.obj \ + $O\StringConvert.obj \ + $O\StringToInt.obj \ + $O\UTFConvert.obj \ + $O\MyVector.obj \ + $O\Wildcard.obj \ + +WIN_OBJS = \ + $O\DLL.obj \ + $O\Error.obj \ + $O\FileDir.obj \ + $O\FileFind.obj \ + $O\FileIO.obj \ + $O\FileName.obj \ + $O\MemoryLock.obj \ + $O\PropVariant.obj \ + $O\PropVariantConversions.obj \ + $O\Synchronization.obj \ + $O\System.obj \ + +7ZIP_COMMON_OBJS = \ + $O\CreateCoder.obj \ + $O\FilePathAutoRename.obj \ + $O\FileStreams.obj \ + $O\InBuffer.obj \ + $O\InOutTempBuffer.obj \ + $O\FilterCoder.obj \ + $O\LimitedStreams.obj \ + $O\LockedStream.obj \ + $O\MethodId.obj \ + $O\MethodProps.obj \ + $O\OffsetStream.obj \ + $O\OutBuffer.obj \ + $O\ProgressUtils.obj \ + $O\StreamBinder.obj \ + $O\StreamObjects.obj \ + $O\StreamUtils.obj \ + $O\VirtThread.obj \ + +UI_COMMON_OBJS = \ + $O\ArchiveCommandLine.obj \ + $O\ArchiveExtractCallback.obj \ + $O\ArchiveOpenCallback.obj \ + $O\DefaultName.obj \ + $O\EnumDirItems.obj \ + $O\Extract.obj \ + $O\ExtractingFilePath.obj \ + $O\LoadCodecs.obj \ + $O\OpenArchive.obj \ + $O\PropIDUtils.obj \ + $O\SetProperties.obj \ + $O\SortUtils.obj \ + $O\TempFiles.obj \ + $O\Update.obj \ + $O\UpdateAction.obj \ + $O\UpdateCallback.obj \ + $O\UpdatePair.obj \ + $O\UpdateProduce.obj \ + $O\WorkDir.obj \ + +AR_COMMON_OBJS = \ + $O\CoderMixer2.obj \ + $O\CoderMixer2MT.obj \ + $O\CrossThreadProgress.obj \ + $O\DummyOutStream.obj \ + $O\HandlerOut.obj \ + $O\InStreamWithCRC.obj \ + $O\ItemNameUtils.obj \ + $O\MultiStream.obj \ + $O\OutStreamWithCRC.obj \ + $O\ParseProperties.obj \ + + +7Z_OBJS = \ + $O\7zCompressionMode.obj \ + $O\7zDecode.obj \ + $O\7zEncode.obj \ + $O\7zExtract.obj \ + $O\7zFolderInStream.obj \ + $O\7zFolderOutStream.obj \ + $O\7zHandler.obj \ + $O\7zHandlerOut.obj \ + $O\7zHeader.obj \ + $O\7zIn.obj \ + $O\7zOut.obj \ + $O\7zProperties.obj \ + $O\7zRegister.obj \ + $O\7zSpecStream.obj \ + $O\7zUpdate.obj \ + + +BRANCH_OPT_OBJS = \ + $O\BranchCoder.obj \ + $O\x86.obj \ + $O\x86_2.obj \ + $O\ARM.obj \ + $O\ARMThumb.obj \ + $O\IA64.obj \ + $O\PPC.obj \ + $O\SPARC.obj \ + $O\BranchRegister.obj \ + $O\BCJRegister.obj \ + $O\BCJ2Register.obj \ + +SWAP_OPT_OBJS = \ + $O\ByteSwap.obj \ + $O\ByteSwapRegister.obj \ + +COPY_OBJS = \ + $O\CopyCoder.obj \ + $O\CopyRegister.obj \ + +LZ_OBJS = \ + $O\LZOutWindow.obj \ + +LZMA_OPT_OBJS = \ + $O\LZMADecoder.obj \ + $O\LZMAEncoder.obj \ + $O\LZMARegister.obj \ + +LZMA_BENCH_OBJS = \ + $O\LzmaBench.obj \ + $O\LzmaBenchCon.obj \ + +C_OBJS = \ + $O\Alloc.obj \ + $O\7zCrc.obj \ + $O\Sort.obj \ + $O\Threads.obj \ + +C_LZ_OBJS = \ + $O\MatchFinder.obj \ + $O\MatchFinderMt.obj \ + +C_BRANCH_OBJS = \ + $O\BranchARM.obj \ + $O\BranchARMThumb.obj \ + $O\BranchIA64.obj \ + $O\BranchPPC.obj \ + $O\BranchSPARC.obj \ + $O\BranchX86.obj \ + +OBJS = \ + $O\StdAfx.obj \ + $(CONSOLE_OBJS) \ + $(COMMON_OBJS) \ + $(WIN_OBJS) \ + $(7ZIP_COMMON_OBJS) \ + $(UI_COMMON_OBJS) \ + $(AR_COMMON_OBJS) \ + $(7Z_OBJS) \ + $(BRANCH_OPT_OBJS) \ + $(SWAP_OPT_OBJS) \ + $(COPY_OBJS) \ + $(LZ_OBJS) \ + $(LZMA_OPT_OBJS) \ + $(LZMA_BENCH_OBJS) \ + $(C_OBJS) \ + $(C_LZ_OBJS) \ + $(C_BRANCH_OBJS) \ + $O\RangeCoderBit.obj \ + $(CRC_OBJS) \ + $O\resource.res + + +!include "../../../Build.mak" + +$(CONSOLE_OBJS): ../../UI/Console/$(*B).cpp + $(COMPL) + +$(COMMON_OBJS): ../../../Common/$(*B).cpp + $(COMPL) +$(WIN_OBJS): ../../../Windows/$(*B).cpp + $(COMPL) +$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp + $(COMPL) +$(UI_COMMON_OBJS): ../../UI/Common/$(*B).cpp + $(COMPL) +$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp + $(COMPL) + +$(7Z_OBJS): ../../Archive/7z/$(*B).cpp + $(COMPL) +$(BRANCH_OPT_OBJS): ../../Compress/Branch/$(*B).cpp + $(COMPL_O2) +$(SWAP_OPT_OBJS): ../../Compress/ByteSwap/$(*B).cpp + $(COMPL_O2) +$(COPY_OBJS): ../../Compress/Copy/$(*B).cpp + $(COMPL) +$(LZ_OBJS): ../../Compress/LZ/$(*B).cpp + $(COMPL) +$(LZMA_OPT_OBJS): ../../Compress/LZMA/$(*B).cpp + $(COMPL_O2) +$(LZMA_BENCH_OBJS): ../../Compress/LZMA_Alone/$(*B).cpp + $(COMPL) +$O\RangeCoderBit.obj: ../../Compress/RangeCoder/$(*B).cpp + $(COMPL) + +$(C_OBJS): ../../../../C/$(*B).c + $(COMPL_O2) +$(C_LZ_OBJS): ../../../../C/Compress/Lz/$(*B).c + $(COMPL_O2) +$(C_BRANCH_OBJS): ../../../../C/Compress/Branch/$(*B).c + $(COMPL_O2) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/resource.rc b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/resource.rc new file mode 100644 index 0000000..fc9063c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Alone7z/resource.rc @@ -0,0 +1,3 @@ +#include "../../MyVersionInfo.rc" + +MY_VERSION_INFO_APP("7-Zip Standalone Console", "7za") diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h new file mode 100644 index 0000000..2e4be10 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" +#include "../../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile new file mode 100644 index 0000000..0632e71 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile @@ -0,0 +1,165 @@ +PROG = 7zxr.dll +DEF_FILE = ../../Archive/Archive2.def +LIBS = $(LIBS) user32.lib oleaut32.lib +CFLAGS = $(CFLAGS) -I ../../../ \ + -DEXTRACT_ONLY \ + -DCOMPRESS_MT \ + -D_NO_CRYPTO + +COMMON_OBJS = \ + $O\CRC.obj \ + $O\IntToString.obj \ + $O\NewHandler.obj \ + $O\MyString.obj \ + $O\StringConvert.obj \ + $O\StringToInt.obj \ + $O\MyVector.obj \ + $O\Wildcard.obj \ + +WIN_OBJS = \ + $O\FileDir.obj \ + $O\FileFind.obj \ + $O\FileIO.obj \ + $O\PropVariant.obj \ + $O\Synchronization.obj \ + $O\System.obj \ + +7ZIP_COMMON_OBJS = \ + $O\CreateCoder.obj \ + $O\InBuffer.obj \ + $O\InOutTempBuffer.obj \ + $O\FilterCoder.obj \ + $O\LimitedStreams.obj \ + $O\LockedStream.obj \ + $O\MethodId.obj \ + $O\MethodProps.obj \ + $O\OutBuffer.obj \ + $O\ProgressUtils.obj \ + $O\StreamBinder.obj \ + $O\StreamObjects.obj \ + $O\StreamUtils.obj \ + $O\VirtThread.obj \ + +AR_OBJS = \ + $O\ArchiveExports.obj \ + $O\DllExports2.obj \ + +AR_COMMON_OBJS = \ + $O\CoderMixer2.obj \ + $O\CoderMixer2MT.obj \ + $O\CrossThreadProgress.obj \ + $O\HandlerOut.obj \ + $O\ItemNameUtils.obj \ + $O\OutStreamWithCRC.obj \ + $O\ParseProperties.obj \ + + +7Z_OBJS = \ + $O\7zCompressionMode.obj \ + $O\7zDecode.obj \ + $O\7zExtract.obj \ + $O\7zFolderOutStream.obj \ + $O\7zHandler.obj \ + $O\7zHeader.obj \ + $O\7zIn.obj \ + $O\7zProperties.obj \ + $O\7zRegister.obj \ + + +COMPRESS_OBJS = \ + $O\CodecExports.obj \ + +SWAP_OPT_OBJS = \ + $O\ByteSwap.obj \ + $O\ByteSwapRegister.obj \ + +BRANCH_OPT_OBJS = \ + $O\BranchCoder.obj \ + $O\x86.obj \ + $O\x86_2.obj \ + $O\ARM.obj \ + $O\ARMThumb.obj \ + $O\IA64.obj \ + $O\PPC.obj \ + $O\SPARC.obj \ + $O\BranchRegister.obj \ + $O\BCJRegister.obj \ + $O\BCJ2Register.obj \ + +COPY_OBJS = \ + $O\CopyCoder.obj \ + $O\CopyRegister.obj \ + +LZ_OBJS = \ + $O\LZOutWindow.obj \ + +LZMA_OPT_OBJS = \ + $O\LZMADecoder.obj \ + $O\LZMARegister.obj \ + +C_OBJS = \ + $O\Alloc.obj \ + $O\7zCrc.obj \ + $O\Threads.obj \ + +C_BRANCH_OBJS = \ + $O\BranchARM.obj \ + $O\BranchARMThumb.obj \ + $O\BranchIA64.obj \ + $O\BranchPPC.obj \ + $O\BranchSPARC.obj \ + $O\BranchX86.obj \ + +OBJS = \ + $O\StdAfx.obj \ + $(CONSOLE_OBJS) \ + $(COMMON_OBJS) \ + $(WIN_OBJS) \ + $(7ZIP_COMMON_OBJS) \ + $(AR_OBJS) \ + $(AR_COMMON_OBJS) \ + $(7Z_OBJS) \ + $(COMPRESS_OBJS) \ + $(BRANCH_OPT_OBJS) \ + $(SWAP_OPT_OBJS) \ + $(COPY_OBJS) \ + $(LZ_OBJS) \ + $(LZMA_OPT_OBJS) \ + $(C_OBJS) \ + $(C_BRANCH_OBJS) \ + $O\resource.res + + +!include "../../../Build.mak" + +$(COMMON_OBJS): ../../../Common/$(*B).cpp + $(COMPL) +$(WIN_OBJS): ../../../Windows/$(*B).cpp + $(COMPL) +$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp + $(COMPL) +$(AR_OBJS): ../../Archive/$(*B).cpp + $(COMPL) +$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp + $(COMPL) + +$(7Z_OBJS): ../../Archive/7z/$(*B).cpp + $(COMPL) + +$(COMPRESS_OBJS): ../../Compress/$(*B).cpp + $(COMPL) +$(BRANCH_OPT_OBJS): ../../Compress/Branch/$(*B).cpp + $(COMPL_O2) +$(SWAP_OPT_OBJS): ../../Compress/ByteSwap/$(*B).cpp + $(COMPL_O2) +$(COPY_OBJS): ../../Compress/Copy/$(*B).cpp + $(COMPL) +$(LZ_OBJS): ../../Compress/LZ/$(*B).cpp + $(COMPL) +$(LZMA_OPT_OBJS): ../../Compress/LZMA/$(*B).cpp + $(COMPL_O2) + +$(C_OBJS): ../../../../C/$(*B).c + $(COMPL_O2) +$(C_BRANCH_OBJS): ../../../../C/Compress/Branch/$(*B).c + $(COMPL_O2) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc new file mode 100644 index 0000000..09708cf --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc @@ -0,0 +1,5 @@ +#include "../../MyVersionInfo.rc" + +MY_VERSION_INFO_DLL("7z Standalone Extracting Plugin", "7zxr") + +101 ICON "../../Archive/7z/7z.ico" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h new file mode 100644 index 0000000..2e4be10 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" +#include "../../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/makefile b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/makefile new file mode 100644 index 0000000..6e96a8a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/makefile @@ -0,0 +1,188 @@ +PROG = 7zra.dll +DEF_FILE = ../../Archive/Archive2.def +LIBS = $(LIBS) user32.lib oleaut32.lib +CFLAGS = $(CFLAGS) -I ../../../ \ + -DCOMPRESS_MT \ + -DCOMPRESS_MF_MT \ + -D_NO_CRYPTO + +COMMON_OBJS = \ + $O\CRC.obj \ + $O\IntToString.obj \ + $O\NewHandler.obj \ + $O\MyString.obj \ + $O\StringConvert.obj \ + $O\StringToInt.obj \ + $O\MyVector.obj \ + $O\Wildcard.obj \ + +WIN_OBJS = \ + $O\FileDir.obj \ + $O\FileFind.obj \ + $O\FileIO.obj \ + $O\PropVariant.obj \ + $O\Synchronization.obj \ + $O\System.obj \ + +7ZIP_COMMON_OBJS = \ + $O\CreateCoder.obj \ + $O\InBuffer.obj \ + $O\InOutTempBuffer.obj \ + $O\FilterCoder.obj \ + $O\LimitedStreams.obj \ + $O\LockedStream.obj \ + $O\MethodId.obj \ + $O\MethodProps.obj \ + $O\OutBuffer.obj \ + $O\ProgressUtils.obj \ + $O\StreamBinder.obj \ + $O\StreamObjects.obj \ + $O\StreamUtils.obj \ + $O\VirtThread.obj \ + +AR_OBJS = \ + $O\ArchiveExports.obj \ + $O\DllExports2.obj \ + +AR_COMMON_OBJS = \ + $O\CoderMixer2.obj \ + $O\CoderMixer2MT.obj \ + $O\CrossThreadProgress.obj \ + $O\HandlerOut.obj \ + $O\InStreamWithCRC.obj \ + $O\ItemNameUtils.obj \ + $O\OutStreamWithCRC.obj \ + $O\ParseProperties.obj \ + + +7Z_OBJS = \ + $O\7zCompressionMode.obj \ + $O\7zDecode.obj \ + $O\7zEncode.obj \ + $O\7zExtract.obj \ + $O\7zFolderInStream.obj \ + $O\7zFolderOutStream.obj \ + $O\7zHandler.obj \ + $O\7zHandlerOut.obj \ + $O\7zHeader.obj \ + $O\7zIn.obj \ + $O\7zOut.obj \ + $O\7zProperties.obj \ + $O\7zSpecStream.obj \ + $O\7zUpdate.obj \ + $O\7zRegister.obj \ + + +COMPRESS_OBJS = \ + $O\CodecExports.obj \ + +BRANCH_OPT_OBJS = \ + $O\BranchCoder.obj \ + $O\x86.obj \ + $O\x86_2.obj \ + $O\ARM.obj \ + $O\ARMThumb.obj \ + $O\IA64.obj \ + $O\PPC.obj \ + $O\SPARC.obj \ + $O\BranchRegister.obj \ + $O\BCJRegister.obj \ + $O\BCJ2Register.obj \ + +SWAP_OPT_OBJS = \ + $O\ByteSwap.obj \ + $O\ByteSwapRegister.obj \ + +COPY_OBJS = \ + $O\CopyCoder.obj \ + $O\CopyRegister.obj \ + +LZ_OBJS = \ + $O\LZOutWindow.obj \ + +LZMA_OPT_OBJS = \ + $O\LZMADecoder.obj \ + $O\LZMAEncoder.obj \ + $O\LZMARegister.obj \ + +C_OBJS = \ + $O\Alloc.obj \ + $O\7zCrc.obj \ + $O\Sort.obj \ + $O\Threads.obj \ + +C_LZ_OBJS = \ + $O\MatchFinder.obj \ + $O\MatchFinderMt.obj \ + +C_BRANCH_OBJS = \ + $O\BranchARM.obj \ + $O\BranchARMThumb.obj \ + $O\BranchIA64.obj \ + $O\BranchPPC.obj \ + $O\BranchSPARC.obj \ + $O\BranchX86.obj \ + +OBJS = \ + $O\StdAfx.obj \ + $(CONSOLE_OBJS) \ + $(COMMON_OBJS) \ + $(WIN_OBJS) \ + $(7ZIP_COMMON_OBJS) \ + $(AR_OBJS) \ + $(AR_COMMON_OBJS) \ + $(7Z_OBJS) \ + $(BZIP2_OBJS) \ + $(BZIP2_OPT_OBJS) \ + $(COMPRESS_OBJS) \ + $(BRANCH_OPT_OBJS) \ + $(SWAP_OPT_OBJS) \ + $(COPY_OBJS) \ + $(DEFLATE_OPT_OBJS) \ + $(LZ_OBJS) \ + $(LZMA_OPT_OBJS) \ + $(PPMD_OPT_OBJS) \ + $(C_OBJS) \ + $(C_LZ_OBJS) \ + $(C_BRANCH_OBJS) \ + $O\RangeCoderBit.obj \ + $O\resource.res + + +!include "../../../Build.mak" + +$(COMMON_OBJS): ../../../Common/$(*B).cpp + $(COMPL) +$(WIN_OBJS): ../../../Windows/$(*B).cpp + $(COMPL) +$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp + $(COMPL) +$(AR_OBJS): ../../Archive/$(*B).cpp + $(COMPL) +$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp + $(COMPL) + +$(7Z_OBJS): ../../Archive/7z/$(*B).cpp + $(COMPL) + +$(COMPRESS_OBJS): ../../Compress/$(*B).cpp + $(COMPL) +$(BRANCH_OPT_OBJS): ../../Compress/Branch/$(*B).cpp + $(COMPL_O2) +$(SWAP_OPT_OBJS): ../../Compress/ByteSwap/$(*B).cpp + $(COMPL_O2) +$(COPY_OBJS): ../../Compress/Copy/$(*B).cpp + $(COMPL) +$(LZ_OBJS): ../../Compress/LZ/$(*B).cpp + $(COMPL) +$(LZMA_OPT_OBJS): ../../Compress/LZMA/$(*B).cpp + $(COMPL_O2) +$O\RangeCoderBit.obj: ../../Compress/RangeCoder/$(*B).cpp + $(COMPL) + +$(C_OBJS): ../../../../C/$(*B).c + $(COMPL_O2) +$(C_LZ_OBJS): ../../../../C/Compress/Lz/$(*B).c + $(COMPL_O2) +$(C_BRANCH_OBJS): ../../../../C/Compress/Branch/$(*B).c + $(COMPL_O2) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/resource.rc b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/resource.rc new file mode 100644 index 0000000..60a17fe --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Bundles/Format7zR/resource.rc @@ -0,0 +1,5 @@ +#include "../../MyVersionInfo.rc" + +MY_VERSION_INFO_DLL("7z Standalone Plugin", "7zr") + +101 ICON "../../Archive/7z/7z.ico" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.cpp new file mode 100644 index 0000000..ae0f98f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.cpp @@ -0,0 +1,292 @@ +// CreateCoder.cpp + +#include "StdAfx.h" + +#include "CreateCoder.h" + +#include "../../Windows/PropVariant.h" +#include "../../Windows/Defs.h" +#include "FilterCoder.h" +#include "RegisterCodec.h" + +static const unsigned int kNumCodecsMax = 64; +unsigned int g_NumCodecs = 0; +const CCodecInfo *g_Codecs[kNumCodecsMax]; +void RegisterCodec(const CCodecInfo *codecInfo) +{ + if (g_NumCodecs < kNumCodecsMax) + g_Codecs[g_NumCodecs++] = codecInfo; +} + +#ifdef EXTERNAL_CODECS +static HRESULT ReadNumberOfStreams(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, UInt32 &res) +{ + NWindows::NCOM::CPropVariant prop; + RINOK(codecsInfo->GetProperty(index, propID, &prop)); + if (prop.vt == VT_EMPTY) + res = 1; + else if (prop.vt == VT_UI4) + res = prop.ulVal; + else + return E_INVALIDARG; + return S_OK; +} + +static HRESULT ReadIsAssignedProp(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, bool &res) +{ + NWindows::NCOM::CPropVariant prop; + RINOK(codecsInfo->GetProperty(index, propID, &prop)); + if (prop.vt == VT_EMPTY) + res = true; + else if (prop.vt == VT_BOOL) + res = VARIANT_BOOLToBool(prop.boolVal); + else + return E_INVALIDARG; + return S_OK; +} + +HRESULT LoadExternalCodecs(ICompressCodecsInfo *codecsInfo, CObjectVector &externalCodecs) +{ + UInt32 num; + RINOK(codecsInfo->GetNumberOfMethods(&num)); + for (UInt32 i = 0; i < num; i++) + { + CCodecInfoEx info; + NWindows::NCOM::CPropVariant prop; + RINOK(codecsInfo->GetProperty(i, NMethodPropID::kID, &prop)); + // if (prop.vt != VT_BSTR) + // info.Id.IDSize = (Byte)SysStringByteLen(prop.bstrVal); + // memmove(info.Id.ID, prop.bstrVal, info.Id.IDSize); + if (prop.vt != VT_UI8) + { + continue; // old Interface + // return E_INVALIDARG; + } + info.Id = prop.uhVal.QuadPart; + prop.Clear(); + + RINOK(codecsInfo->GetProperty(i, NMethodPropID::kName, &prop)); + if (prop.vt == VT_BSTR) + info.Name = prop.bstrVal; + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG;; + + RINOK(ReadNumberOfStreams(codecsInfo, i, NMethodPropID::kInStreams, info.NumInStreams)); + RINOK(ReadNumberOfStreams(codecsInfo, i, NMethodPropID::kOutStreams, info.NumOutStreams)); + RINOK(ReadIsAssignedProp(codecsInfo, i, NMethodPropID::kEncoderIsAssigned, info.EncoderIsAssigned)); + RINOK(ReadIsAssignedProp(codecsInfo, i, NMethodPropID::kDecoderIsAssigned, info.DecoderIsAssigned)); + + externalCodecs.Add(info); + } + return S_OK; +} + +#endif + +bool FindMethod( + #ifdef EXTERNAL_CODECS + ICompressCodecsInfo * /* codecsInfo */, const CObjectVector *externalCodecs, + #endif + const UString &name, + CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams) +{ + UInt32 i; + for (i = 0; i < g_NumCodecs; i++) + { + const CCodecInfo &codec = *g_Codecs[i]; + if (name.CompareNoCase(codec.Name) == 0) + { + methodId = codec.Id; + numInStreams = codec.NumInStreams; + numOutStreams = 1; + return true; + } + } + #ifdef EXTERNAL_CODECS + if (externalCodecs) + for (i = 0; i < (UInt32)externalCodecs->Size(); i++) + { + const CCodecInfoEx &codec = (*externalCodecs)[i]; + if (codec.Name.CompareNoCase(name) == 0) + { + methodId = codec.Id; + numInStreams = codec.NumInStreams; + numOutStreams = codec.NumOutStreams; + return true; + } + } + #endif + return false; +} + +bool FindMethod( + #ifdef EXTERNAL_CODECS + ICompressCodecsInfo * /* codecsInfo */, const CObjectVector *externalCodecs, + #endif + CMethodId methodId, UString &name) +{ + UInt32 i; + for (i = 0; i < g_NumCodecs; i++) + { + const CCodecInfo &codec = *g_Codecs[i]; + if (methodId == codec.Id) + { + name = codec.Name; + return true; + } + } + #ifdef EXTERNAL_CODECS + if (externalCodecs) + for (i = 0; i < (UInt32)externalCodecs->Size(); i++) + { + const CCodecInfoEx &codec = (*externalCodecs)[i]; + if (methodId == codec.Id) + { + name = codec.Name; + return true; + } + } + #endif + return false; +} + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &filter, + CMyComPtr &coder, + CMyComPtr &coder2, + bool encode, bool onlyCoder) +{ + bool created = false; + UInt32 i; + for (i = 0; i < g_NumCodecs; i++) + { + const CCodecInfo &codec = *g_Codecs[i]; + if (codec.Id == methodId) + { + if (encode) + { + if (codec.CreateEncoder) + { + void *p = codec.CreateEncoder(); + if (codec.IsFilter) filter = (ICompressFilter *)p; + else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; + else coder2 = (ICompressCoder2 *)p; + created = (p != 0); + break; + } + } + else + if (codec.CreateDecoder) + { + void *p = codec.CreateDecoder(); + if (codec.IsFilter) filter = (ICompressFilter *)p; + else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; + else coder2 = (ICompressCoder2 *)p; + created = (p != 0); + break; + } + } + } + + #ifdef EXTERNAL_CODECS + if (!created && externalCodecs) + for (i = 0; i < (UInt32)externalCodecs->Size(); i++) + { + const CCodecInfoEx &codec = (*externalCodecs)[i]; + if (codec.Id == methodId) + { + if (encode) + { + if (codec.EncoderIsAssigned) + { + if (codec.IsSimpleCodec()) + { + HRESULT result = codecsInfo->CreateEncoder(i, &IID_ICompressCoder, (void **)&coder); + if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) + return result; + if (!coder) + { + RINOK(codecsInfo->CreateEncoder(i, &IID_ICompressFilter, (void **)&filter)); + } + } + else + { + RINOK(codecsInfo->CreateEncoder(i, &IID_ICompressCoder2, (void **)&coder2)); + } + break; + } + } + else + if (codec.DecoderIsAssigned) + { + if (codec.IsSimpleCodec()) + { + HRESULT result = codecsInfo->CreateDecoder(i, &IID_ICompressCoder, (void **)&coder); + if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) + return result; + if (!coder) + { + RINOK(codecsInfo->CreateDecoder(i, &IID_ICompressFilter, (void **)&filter)); + } + } + else + { + RINOK(codecsInfo->CreateDecoder(i, &IID_ICompressCoder2, (void **)&coder2)); + } + break; + } + } + } + #endif + + if (onlyCoder && filter) + { + CFilterCoder *coderSpec = new CFilterCoder; + coder = coderSpec; + coderSpec->Filter = filter; + } + return S_OK; +} + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &coder, + CMyComPtr &coder2, + bool encode) +{ + CMyComPtr filter; + return CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodId, + filter, coder, coder2, encode, true); +} + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &coder, bool encode) +{ + CMyComPtr filter; + CMyComPtr coder2; + return CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodId, + coder, coder2, encode); +} + +HRESULT CreateFilter( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &filter, + bool encode) +{ + CMyComPtr coder; + CMyComPtr coder2; + return CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodId, + filter, coder, coder2, encode, false); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.h b/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.h new file mode 100644 index 0000000..4d86ae7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/CreateCoder.h @@ -0,0 +1,98 @@ +// CreateCoder.h + +#ifndef __CREATECODER_H +#define __CREATECODER_H + +#include "Common/MyCom.h" +#include "Common/MyString.h" +#include "../ICoder.h" + +#include "MethodId.h" + +#ifdef EXTERNAL_CODECS + +struct CCodecInfoEx +{ + UString Name; + CMethodId Id; + UInt32 NumInStreams; + UInt32 NumOutStreams; + bool EncoderIsAssigned; + bool DecoderIsAssigned; + bool IsSimpleCodec() const { return NumOutStreams == 1 && NumInStreams == 1; } + CCodecInfoEx(): EncoderIsAssigned(false), DecoderIsAssigned(false) {} +}; + +HRESULT LoadExternalCodecs(ICompressCodecsInfo *codecsInfo, CObjectVector &externalCodecs); + +#define PUBLIC_ISetCompressCodecsInfo public ISetCompressCodecsInfo, +#define QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_ENTRY(ISetCompressCodecsInfo) +#define DECL_ISetCompressCodecsInfo STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo); +#define IMPL_ISetCompressCodecsInfo2(x) \ +STDMETHODIMP x::SetCompressCodecsInfo(ICompressCodecsInfo *compressCodecsInfo) { \ + COM_TRY_BEGIN _codecsInfo = compressCodecsInfo; return LoadExternalCodecs(_codecsInfo, _externalCodecs); COM_TRY_END } +#define IMPL_ISetCompressCodecsInfo IMPL_ISetCompressCodecsInfo2(CHandler) + +#define EXTERNAL_CODECS_VARS2 _codecsInfo, &_externalCodecs + +#define DECL_EXTERNAL_CODECS_VARS CMyComPtr _codecsInfo; CObjectVector _externalCodecs; +#define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2, + +#define DECL_EXTERNAL_CODECS_LOC_VARS2 ICompressCodecsInfo *codecsInfo, const CObjectVector *externalCodecs +#define EXTERNAL_CODECS_LOC_VARS2 codecsInfo, externalCodecs + +#define DECL_EXTERNAL_CODECS_LOC_VARS DECL_EXTERNAL_CODECS_LOC_VARS2, +#define EXTERNAL_CODECS_LOC_VARS EXTERNAL_CODECS_LOC_VARS2, + +#else + +#define PUBLIC_ISetCompressCodecsInfo +#define QUERY_ENTRY_ISetCompressCodecsInfo +#define DECL_ISetCompressCodecsInfo +#define IMPL_ISetCompressCodecsInfo +#define EXTERNAL_CODECS_VARS2 +#define DECL_EXTERNAL_CODECS_VARS +#define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2 +#define DECL_EXTERNAL_CODECS_LOC_VARS2 +#define EXTERNAL_CODECS_LOC_VARS2 +#define DECL_EXTERNAL_CODECS_LOC_VARS +#define EXTERNAL_CODECS_LOC_VARS + +#endif + +bool FindMethod( + DECL_EXTERNAL_CODECS_LOC_VARS + const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams); + +bool FindMethod( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, UString &name); + + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &filter, + CMyComPtr &coder, + CMyComPtr &coder2, + bool encode, bool onlyCoder); + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &coder, + CMyComPtr &coder2, + bool encode); + +HRESULT CreateCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &coder, bool encode); + +HRESULT CreateFilter( + DECL_EXTERNAL_CODECS_LOC_VARS + CMethodId methodId, + CMyComPtr &filter, + bool encode); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.cpp new file mode 100644 index 0000000..3c5b910 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.cpp @@ -0,0 +1,57 @@ +// FilePathAutoRename.cpp + +#include "StdAfx.h" +#include "FilePathAutoRename.h" + +#include "Common/Defs.h" +#include "Common/IntToString.h" + +#include "Windows/FileName.h" +#include "Windows/FileFind.h" + +using namespace NWindows; + +static bool MakeAutoName(const UString &name, + const UString &extension, int value, UString &path) +{ + wchar_t number[32]; + ConvertUInt64ToString(value, number); + path = name; + path += number; + path += extension; + return NFile::NFind::DoesFileExist(path); +} + +bool AutoRenamePath(UString &fullProcessedPath) +{ + UString path; + int dotPos = fullProcessedPath.ReverseFind(L'.'); + + int slashPos = fullProcessedPath.ReverseFind(L'/'); + #ifdef _WIN32 + int slash1Pos = fullProcessedPath.ReverseFind(L'\\'); + slashPos = MyMax(slashPos, slash1Pos); + #endif + + UString name, extension; + if (dotPos > slashPos && dotPos > 0) + { + name = fullProcessedPath.Left(dotPos); + extension = fullProcessedPath.Mid(dotPos); + } + else + name = fullProcessedPath; + name += L'_'; + int indexLeft = 1, indexRight = (1 << 30); + while (indexLeft != indexRight) + { + int indexMid = (indexLeft + indexRight) / 2; + if (MakeAutoName(name, extension, indexMid, path)) + indexLeft = indexMid + 1; + else + indexRight = indexMid; + } + if (MakeAutoName(name, extension, indexRight, fullProcessedPath)) + return false; + return true; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.h b/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.h new file mode 100644 index 0000000..3ef87f4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FilePathAutoRename.h @@ -0,0 +1,10 @@ +// Util/FilePathAutoRename.h + +#ifndef __FILEPATHAUTORENAME_H +#define __FILEPATHAUTORENAME_H + +#include "Common/MyString.h" + +bool AutoRenamePath(UString &fullProcessedPath); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.cpp new file mode 100644 index 0000000..bebcf14 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.cpp @@ -0,0 +1,261 @@ +// FileStreams.cpp + +#include "StdAfx.h" + +#ifndef _WIN32 +#include +#include +#include +#endif + +#include "FileStreams.h" + +static inline HRESULT ConvertBoolToHRESULT(bool result) +{ + #ifdef _WIN32 + if (result) + return S_OK; + DWORD lastError = ::GetLastError(); + if (lastError == 0) + return E_FAIL; + return lastError; + #else + return result ? S_OK: E_FAIL; + #endif +} + +bool CInFileStream::Open(LPCTSTR fileName) +{ + return File.Open(fileName); +} + +#ifdef USE_WIN_FILE +#ifndef _UNICODE +bool CInFileStream::Open(LPCWSTR fileName) +{ + return File.Open(fileName); +} +#endif +#endif + +bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite) +{ + return File.OpenShared(fileName, shareForWrite); +} + +#ifdef USE_WIN_FILE +#ifndef _UNICODE +bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite) +{ + return File.OpenShared(fileName, shareForWrite); +} +#endif +#endif + +STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + #ifdef USE_WIN_FILE + + UInt32 realProcessedSize; + bool result = File.ReadPart(data, size, realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + return ConvertBoolToHRESULT(result); + + #else + + if(processedSize != NULL) + *processedSize = 0; + ssize_t res = File.Read(data, (size_t)size); + if (res == -1) + return E_FAIL; + if(processedSize != NULL) + *processedSize = (UInt32)res; + return S_OK; + + #endif +} + +#ifndef _WIN32_WCE +STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + #ifdef _WIN32 + UInt32 realProcessedSize; + BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), + data, size, (DWORD *)&realProcessedSize, NULL); + if(processedSize != NULL) + *processedSize = realProcessedSize; + if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) + return S_OK; + return ConvertBoolToHRESULT(res != FALSE); + + #else + + if(processedSize != NULL) + *processedSize = 0; + ssize_t res; + do + { + res = read(0, data, (size_t)size); + } + while (res < 0 && (errno == EINTR)); + if (res == -1) + return E_FAIL; + if(processedSize != NULL) + *processedSize = (UInt32)res; + return S_OK; + + #endif +} + +#endif + +STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, + UInt64 *newPosition) +{ + if(seekOrigin >= 3) + return STG_E_INVALIDFUNCTION; + + #ifdef USE_WIN_FILE + + UInt64 realNewPosition; + bool result = File.Seek(offset, seekOrigin, realNewPosition); + if(newPosition != NULL) + *newPosition = realNewPosition; + return ConvertBoolToHRESULT(result); + + #else + + off_t res = File.Seek(offset, seekOrigin); + if (res == -1) + return E_FAIL; + if(newPosition != NULL) + *newPosition = (UInt64)res; + return S_OK; + + #endif +} + +STDMETHODIMP CInFileStream::GetSize(UInt64 *size) +{ + return ConvertBoolToHRESULT(File.GetLength(*size)); +} + + +////////////////////////// +// COutFileStream + +HRESULT COutFileStream::Close() +{ + return ConvertBoolToHRESULT(File.Close()); +} + +STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + #ifdef USE_WIN_FILE + + UInt32 realProcessedSize; + bool result = File.WritePart(data, size, realProcessedSize); + ProcessedSize += realProcessedSize; + if(processedSize != NULL) + *processedSize = realProcessedSize; + return ConvertBoolToHRESULT(result); + + #else + + if(processedSize != NULL) + *processedSize = 0; + ssize_t res = File.Write(data, (size_t)size); + if (res == -1) + return E_FAIL; + if(processedSize != NULL) + *processedSize = (UInt32)res; + ProcessedSize += res; + return S_OK; + + #endif +} + +STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if(seekOrigin >= 3) + return STG_E_INVALIDFUNCTION; + #ifdef USE_WIN_FILE + + UInt64 realNewPosition; + bool result = File.Seek(offset, seekOrigin, realNewPosition); + if(newPosition != NULL) + *newPosition = realNewPosition; + return ConvertBoolToHRESULT(result); + + #else + + off_t res = File.Seek(offset, seekOrigin); + if (res == -1) + return E_FAIL; + if(newPosition != NULL) + *newPosition = (UInt64)res; + return S_OK; + + #endif +} + +STDMETHODIMP COutFileStream::SetSize(Int64 newSize) +{ + #ifdef USE_WIN_FILE + UInt64 currentPos; + if(!File.Seek(0, FILE_CURRENT, currentPos)) + return E_FAIL; + bool result = File.SetLength(newSize); + UInt64 currentPos2; + result = result && File.Seek(currentPos, currentPos2); + return result ? S_OK : E_FAIL; + #else + return E_FAIL; + #endif +} + +#ifndef _WIN32_WCE +STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + + #ifdef _WIN32 + UInt32 realProcessedSize; + BOOL res = TRUE; + if (size > 0) + { + // Seems that Windows doesn't like big amounts writing to stdout. + // So we limit portions by 32KB. + UInt32 sizeTemp = (1 << 15); + if (sizeTemp > size) + sizeTemp = size; + res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), + data, sizeTemp, (DWORD *)&realProcessedSize, NULL); + size -= realProcessedSize; + data = (const void *)((const Byte *)data + realProcessedSize); + if(processedSize != NULL) + *processedSize += realProcessedSize; + } + return ConvertBoolToHRESULT(res != FALSE); + + #else + + ssize_t res; + do + { + res = write(1, data, (size_t)size); + } + while (res < 0 && (errno == EINTR)); + if (res == -1) + return E_FAIL; + if(processedSize != NULL) + *processedSize = (UInt32)res; + return S_OK; + + return S_OK; + #endif +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.h b/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.h new file mode 100644 index 0000000..715801d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FileStreams.h @@ -0,0 +1,143 @@ +// FileStreams.h + +#ifndef __FILESTREAMS_H +#define __FILESTREAMS_H + +#ifdef _WIN32 +#define USE_WIN_FILE +#endif + +#ifdef USE_WIN_FILE +#include "../../Windows/FileIO.h" +#else +#include "../../Common/C_FileIO.h" +#endif + +#include "../IStream.h" +#include "../../Common/MyCom.h" + +class CInFileStream: + public IInStream, + public IStreamGetSize, + public CMyUnknownImp +{ +public: + #ifdef USE_WIN_FILE + NWindows::NFile::NIO::CInFile File; + #else + NC::NFile::NIO::CInFile File; + #endif + CInFileStream() {} + virtual ~CInFileStream() {} + + bool Open(LPCTSTR fileName); + #ifdef USE_WIN_FILE + #ifndef _UNICODE + bool Open(LPCWSTR fileName); + #endif + #endif + + bool OpenShared(LPCTSTR fileName, bool shareForWrite); + #ifdef USE_WIN_FILE + #ifndef _UNICODE + bool OpenShared(LPCWSTR fileName, bool shareForWrite); + #endif + #endif + + MY_UNKNOWN_IMP2(IInStream, IStreamGetSize) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + + STDMETHOD(GetSize)(UInt64 *size); +}; + +#ifndef _WIN32_WCE +class CStdInFileStream: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + // HANDLE File; + // CStdInFileStream() File(INVALID_HANDLE_VALUE): {} + // void Open() { File = GetStdHandle(STD_INPUT_HANDLE); }; + MY_UNKNOWN_IMP + + virtual ~CStdInFileStream() {} + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; +#endif + +class COutFileStream: + public IOutStream, + public CMyUnknownImp +{ + #ifdef USE_WIN_FILE + NWindows::NFile::NIO::COutFile File; + #else + NC::NFile::NIO::COutFile File; + #endif +public: + virtual ~COutFileStream() {} + bool Create(LPCTSTR fileName, bool createAlways) + { + ProcessedSize = 0; + return File.Create(fileName, createAlways); + } + bool Open(LPCTSTR fileName, DWORD creationDisposition) + { + ProcessedSize = 0; + return File.Open(fileName, creationDisposition); + } + #ifdef USE_WIN_FILE + #ifndef _UNICODE + bool Create(LPCWSTR fileName, bool createAlways) + { + ProcessedSize = 0; + return File.Create(fileName, createAlways); + } + bool Open(LPCWSTR fileName, DWORD creationDisposition) + { + ProcessedSize = 0; + return File.Open(fileName, creationDisposition); + } + #endif + #endif + + HRESULT Close(); + + UInt64 ProcessedSize; + + #ifdef USE_WIN_FILE + bool SetTime(const FILETIME *creationTime, const FILETIME *lastAccessTime, const FILETIME *lastWriteTime) + { + return File.SetTime(creationTime, lastAccessTime, lastWriteTime); + } + bool SetLastWriteTime(const FILETIME *lastWriteTime) + { + return File.SetLastWriteTime(lastWriteTime); + } + #endif + + + MY_UNKNOWN_IMP1(IOutStream) + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + STDMETHOD(SetSize)(Int64 newSize); +}; + +#ifndef _WIN32_WCE +class CStdOutFileStream: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + virtual ~CStdOutFileStream() {} + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.cpp new file mode 100644 index 0000000..fd89d0e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.cpp @@ -0,0 +1,264 @@ +// FilterCoder.cpp + +#include "StdAfx.h" + +#include "FilterCoder.h" +extern "C" +{ +#include "../../../C/Alloc.h" +} +#include "../../Common/Defs.h" +#include "StreamUtils.h" + +static const UInt32 kBufferSize = 1 << 17; + +CFilterCoder::CFilterCoder() +{ + _buffer = (Byte *)::MidAlloc(kBufferSize); +} + +CFilterCoder::~CFilterCoder() +{ + ::MidFree(_buffer); +} + +HRESULT CFilterCoder::WriteWithLimit(ISequentialOutStream *outStream, UInt32 size) +{ + if (_outSizeIsDefined) + { + UInt64 remSize = _outSize - _nowPos64; + if (size > remSize) + size = (UInt32)remSize; + } + UInt32 processedSize = 0; + RINOK(WriteStream(outStream, _buffer, size, &processedSize)); + if (size != processedSize) + return E_FAIL; + _nowPos64 += processedSize; + return S_OK; +} + + +STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + RINOK(Init()); + UInt32 bufferPos = 0; + _outSizeIsDefined = (outSize != 0); + if (_outSizeIsDefined) + _outSize = *outSize; + + while(NeedMore()) + { + UInt32 processedSize; + + // Change it: It can be optimized using ReadPart + RINOK(ReadStream(inStream, _buffer + bufferPos, kBufferSize - bufferPos, &processedSize)); + + UInt32 endPos = bufferPos + processedSize; + + bufferPos = Filter->Filter(_buffer, endPos); + if (bufferPos > endPos) + { + for (; endPos< bufferPos; endPos++) + _buffer[endPos] = 0; + bufferPos = Filter->Filter(_buffer, endPos); + } + + if (bufferPos == 0) + { + if (endPos > 0) + return WriteWithLimit(outStream, endPos); + return S_OK; + } + RINOK(WriteWithLimit(outStream, bufferPos)); + if (progress != NULL) + { + RINOK(progress->SetRatioInfo(&_nowPos64, &_nowPos64)); + } + UInt32 i = 0; + while(bufferPos < endPos) + _buffer[i++] = _buffer[bufferPos++]; + bufferPos = i; + } + return S_OK; +} + +// #ifdef _ST_MODE +STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream) +{ + _bufferPos = 0; + _outStream = outStream; + return Init(); +} + +STDMETHODIMP CFilterCoder::ReleaseOutStream() +{ + _outStream.Release(); + return S_OK; +}; + + +STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 processedSizeTotal = 0; + while(size > 0) + { + UInt32 sizeMax = kBufferSize - _bufferPos; + UInt32 sizeTemp = size; + if (sizeTemp > sizeMax) + sizeTemp = sizeMax; + memmove(_buffer + _bufferPos, data, sizeTemp); + size -= sizeTemp; + processedSizeTotal += sizeTemp; + data = (const Byte *)data + sizeTemp; + UInt32 endPos = _bufferPos + sizeTemp; + _bufferPos = Filter->Filter(_buffer, endPos); + if (_bufferPos == 0) + { + _bufferPos = endPos; + break; + } + if (_bufferPos > endPos) + { + if (size != 0) + return E_FAIL; + break; + } + RINOK(WriteWithLimit(_outStream, _bufferPos)); + UInt32 i = 0; + while(_bufferPos < endPos) + _buffer[i++] = _buffer[_bufferPos++]; + _bufferPos = i; + } + if (processedSize != NULL) + *processedSize = processedSizeTotal; + return S_OK; +} + +STDMETHODIMP CFilterCoder::Flush() +{ + if (_bufferPos != 0) + { + UInt32 endPos = Filter->Filter(_buffer, _bufferPos); + if (endPos > _bufferPos) + { + for (; _bufferPos < endPos; _bufferPos++) + _buffer[_bufferPos] = 0; + if (Filter->Filter(_buffer, endPos) != endPos) + return E_FAIL; + } + UInt32 processedSize; + RINOK(WriteStream(_outStream, _buffer, _bufferPos, &processedSize)); + if (_bufferPos != processedSize) + return E_FAIL; + _bufferPos = 0; + } + CMyComPtr flush; + _outStream.QueryInterface(IID_IOutStreamFlush, &flush); + if (flush) + return flush->Flush(); + return S_OK; +} + + +STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream) +{ + _convertedPosBegin = _convertedPosEnd = _bufferPos = 0; + _inStream = inStream; + return Init(); +} + +STDMETHODIMP CFilterCoder::ReleaseInStream() +{ + _inStream.Release(); + return S_OK; +}; + +STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 processedSizeTotal = 0; + while(size > 0) + { + if (_convertedPosBegin != _convertedPosEnd) + { + UInt32 sizeTemp = MyMin(size, _convertedPosEnd - _convertedPosBegin); + memmove(data, _buffer + _convertedPosBegin, sizeTemp); + _convertedPosBegin += sizeTemp; + data = (void *)((Byte *)data + sizeTemp); + size -= sizeTemp; + processedSizeTotal += sizeTemp; + break; + } + int i; + for (i = 0; _convertedPosEnd + i < _bufferPos; i++) + _buffer[i] = _buffer[i + _convertedPosEnd]; + _bufferPos = i; + _convertedPosBegin = _convertedPosEnd = 0; + UInt32 processedSizeTemp; + UInt32 size0 = kBufferSize - _bufferPos; + // Optimize it: + RINOK(ReadStream(_inStream, _buffer + _bufferPos, size0, &processedSizeTemp)); + _bufferPos = _bufferPos + processedSizeTemp; + _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); + if (_convertedPosEnd == 0) + { + if (_bufferPos == 0) + break; + else + { + _convertedPosEnd = _bufferPos; // check it + continue; + } + } + if (_convertedPosEnd > _bufferPos) + { + for (; _bufferPos < _convertedPosEnd; _bufferPos++) + _buffer[_bufferPos] = 0; + _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); + } + } + if (processedSize != NULL) + *processedSize = processedSizeTotal; + return S_OK; +} + +// #endif // _ST_MODE + +#ifndef _NO_CRYPTO +STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size) +{ + return _setPassword->CryptoSetPassword(data, size); +} +#endif + +#ifndef EXTRACT_ONLY +STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs, + const PROPVARIANT *properties, UInt32 numProperties) +{ + return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties); +} + +STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream) +{ + return _writeCoderProperties->WriteCoderProperties(outStream); +} + +/* +STDMETHODIMP CFilterCoder::ResetSalt() +{ + return _CryptoResetSalt->ResetSalt(); +} +*/ + +STDMETHODIMP CFilterCoder::ResetInitVector() +{ + return _CryptoResetInitVector->ResetInitVector(); +} +#endif + +STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size) +{ + return _setDecoderProperties->SetDecoderProperties2(data, size); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.h b/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.h new file mode 100644 index 0000000..f654ae3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/FilterCoder.h @@ -0,0 +1,143 @@ +// FilterCoder.h + +#ifndef __FILTERCODER_H +#define __FILTERCODER_H + +#include "../../Common/MyCom.h" +#include "../ICoder.h" +#include "../IPassword.h" + +#define MY_QUERYINTERFACE_ENTRY_AG(i, sub0, sub) if (iid == IID_ ## i) \ +{ if (!sub) RINOK(sub0->QueryInterface(IID_ ## i, (void **)&sub)) \ +*outObject = (void *)(i *)this; AddRef(); return S_OK; } + +class CFilterCoder: + public ICompressCoder, + // #ifdef _ST_MODE + public ICompressSetInStream, + public ISequentialInStream, + public ICompressSetOutStream, + public ISequentialOutStream, + public IOutStreamFlush, + // #endif + + #ifndef _NO_CRYPTO + public ICryptoSetPassword, + #endif + #ifndef EXTRACT_ONLY + public ICompressSetCoderProperties, + public ICompressWriteCoderProperties, + // public ICryptoResetSalt, + public ICryptoResetInitVector, + #endif + public ICompressSetDecoderProperties2, + public CMyUnknownImp +{ +protected: + Byte *_buffer; + // #ifdef _ST_MODE + CMyComPtr _inStream; + CMyComPtr _outStream; + UInt32 _bufferPos; + UInt32 _convertedPosBegin; + UInt32 _convertedPosEnd; + // #endif + bool _outSizeIsDefined; + UInt64 _outSize; + UInt64 _nowPos64; + + HRESULT Init() + { + _nowPos64 = 0; + _outSizeIsDefined = false; + return Filter->Init(); + } + + CMyComPtr _setPassword; + #ifndef EXTRACT_ONLY + CMyComPtr _SetCoderProperties; + CMyComPtr _writeCoderProperties; + // CMyComPtr _CryptoResetSalt; + CMyComPtr _CryptoResetInitVector; + #endif + CMyComPtr _setDecoderProperties; +public: + CMyComPtr Filter; + + CFilterCoder(); + ~CFilterCoder(); + HRESULT WriteWithLimit(ISequentialOutStream *outStream, UInt32 size); + bool NeedMore() const + { return (!_outSizeIsDefined || (_nowPos64 < _outSize)); } + +public: + MY_QUERYINTERFACE_BEGIN + MY_QUERYINTERFACE_ENTRY(ICompressCoder) + // #ifdef _ST_MODE + MY_QUERYINTERFACE_ENTRY(ICompressSetInStream) + MY_QUERYINTERFACE_ENTRY(ISequentialInStream) + + MY_QUERYINTERFACE_ENTRY(ICompressSetOutStream) + MY_QUERYINTERFACE_ENTRY(ISequentialOutStream) + MY_QUERYINTERFACE_ENTRY(IOutStreamFlush) + // #endif + + #ifndef _NO_CRYPTO + MY_QUERYINTERFACE_ENTRY_AG(ICryptoSetPassword, Filter, _setPassword) + #endif + + #ifndef EXTRACT_ONLY + MY_QUERYINTERFACE_ENTRY_AG(ICompressSetCoderProperties, Filter, _SetCoderProperties) + MY_QUERYINTERFACE_ENTRY_AG(ICompressWriteCoderProperties, Filter, _writeCoderProperties) + // MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetSalt, Filter, _CryptoResetSalt) + MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetInitVector, Filter, _CryptoResetInitVector) + #endif + + MY_QUERYINTERFACE_ENTRY_AG(ICompressSetDecoderProperties2, Filter, _setDecoderProperties) + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + // #ifdef _ST_MODE + STDMETHOD(ReleaseInStream)(); + STDMETHOD(SetInStream)(ISequentialInStream *inStream); + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); \ + STDMETHOD(SetOutStream)(ISequentialOutStream *outStream); + STDMETHOD(ReleaseOutStream)(); + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Flush)(); + // #endif + + #ifndef _NO_CRYPTO + STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size); + #endif + #ifndef EXTRACT_ONLY + STDMETHOD(SetCoderProperties)(const PROPID *propIDs, + const PROPVARIANT *properties, UInt32 numProperties); + STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); + // STDMETHOD(ResetSalt)(); + STDMETHOD(ResetInitVector)(); + #endif + STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); +}; + +// #ifdef _ST_MODE +class CInStreamReleaser +{ +public: + CFilterCoder *FilterCoder; + CInStreamReleaser(): FilterCoder(0) {} + ~CInStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseInStream(); } +}; + +class COutStreamReleaser +{ +public: + CFilterCoder *FilterCoder; + COutStreamReleaser(): FilterCoder(0) {} + ~COutStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseOutStream(); } +}; +// #endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.cpp new file mode 100644 index 0000000..5c4361a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.cpp @@ -0,0 +1,83 @@ +// InBuffer.cpp + +#include "StdAfx.h" + +#include "InBuffer.h" + +extern "C" +{ + #include "../../../C/Alloc.h" +} + +CInBuffer::CInBuffer(): + _buffer(0), + _bufferLimit(0), + _bufferBase(0), + _stream(0), + _bufferSize(0) +{} + +bool CInBuffer::Create(UInt32 bufferSize) +{ + const UInt32 kMinBlockSize = 1; + if (bufferSize < kMinBlockSize) + bufferSize = kMinBlockSize; + if (_bufferBase != 0 && _bufferSize == bufferSize) + return true; + Free(); + _bufferSize = bufferSize; + _bufferBase = (Byte *)::MidAlloc(bufferSize); + return (_bufferBase != 0); +} + +void CInBuffer::Free() +{ + ::MidFree(_bufferBase); + _bufferBase = 0; +} + +void CInBuffer::SetStream(ISequentialInStream *stream) +{ + _stream = stream; +} + +void CInBuffer::Init() +{ + _processedSize = 0; + _buffer = _bufferBase; + _bufferLimit = _buffer; + _wasFinished = false; + #ifdef _NO_EXCEPTIONS + ErrorCode = S_OK; + #endif +} + +bool CInBuffer::ReadBlock() +{ + #ifdef _NO_EXCEPTIONS + if (ErrorCode != S_OK) + return false; + #endif + if (_wasFinished) + return false; + _processedSize += (_buffer - _bufferBase); + UInt32 numProcessedBytes; + HRESULT result = _stream->Read(_bufferBase, _bufferSize, &numProcessedBytes); + #ifdef _NO_EXCEPTIONS + ErrorCode = result; + #else + if (result != S_OK) + throw CInBufferException(result); + #endif + _buffer = _bufferBase; + _bufferLimit = _buffer + numProcessedBytes; + _wasFinished = (numProcessedBytes == 0); + return (!_wasFinished); +} + +Byte CInBuffer::ReadBlock2() +{ + if(!ReadBlock()) + return 0xFF; + return *_buffer++; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.h b/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.h new file mode 100644 index 0000000..05fc77e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/InBuffer.h @@ -0,0 +1,75 @@ +// InBuffer.h + +#ifndef __INBUFFER_H +#define __INBUFFER_H + +#include "../IStream.h" +#include "../../Common/MyCom.h" +#include "../../Common/MyException.h" + +#ifndef _NO_EXCEPTIONS +struct CInBufferException: public CSystemException +{ + CInBufferException(HRESULT errorCode): CSystemException(errorCode) {} +}; +#endif + +class CInBuffer +{ + Byte *_buffer; + Byte *_bufferLimit; + Byte *_bufferBase; + CMyComPtr _stream; + UInt64 _processedSize; + UInt32 _bufferSize; + bool _wasFinished; + + bool ReadBlock(); + Byte ReadBlock2(); + +public: + #ifdef _NO_EXCEPTIONS + HRESULT ErrorCode; + #endif + + CInBuffer(); + ~CInBuffer() { Free(); } + + bool Create(UInt32 bufferSize); + void Free(); + + void SetStream(ISequentialInStream *stream); + void Init(); + void ReleaseStream() { _stream.Release(); } + + bool ReadByte(Byte &b) + { + if(_buffer >= _bufferLimit) + if(!ReadBlock()) + return false; + b = *_buffer++; + return true; + } + Byte ReadByte() + { + if(_buffer >= _bufferLimit) + return ReadBlock2(); + return *_buffer++; + } + void ReadBytes(void *data, UInt32 size, UInt32 &processedSize) + { + for(processedSize = 0; processedSize < size; processedSize++) + if (!ReadByte(((Byte *)data)[processedSize])) + return; + } + bool ReadBytes(void *data, UInt32 size) + { + UInt32 processedSize; + ReadBytes(data, size, processedSize); + return (processedSize == size); + } + UInt64 GetProcessedSize() const { return _processedSize + (_buffer - _bufferBase); } + bool WasFinished() const { return _wasFinished; } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.cpp new file mode 100644 index 0000000..ffaed32 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.cpp @@ -0,0 +1,122 @@ +// InOutTempBuffer.cpp + +#include "StdAfx.h" + +#include "InOutTempBuffer.h" +#include "../../Common/Defs.h" +// #include "Windows/Defs.h" + +#include "StreamUtils.h" + +using namespace NWindows; +using namespace NFile; +using namespace NDirectory; + +static UInt32 kTmpBufferMemorySize = (1 << 20); + +static LPCTSTR kTempFilePrefixString = TEXT("iot"); + +CInOutTempBuffer::CInOutTempBuffer(): + _buffer(NULL) +{ +} + +void CInOutTempBuffer::Create() +{ + _buffer = new Byte[kTmpBufferMemorySize]; +} + +CInOutTempBuffer::~CInOutTempBuffer() +{ + delete []_buffer; +} +void CInOutTempBuffer::InitWriting() +{ + _bufferPosition = 0; + _tmpFileCreated = false; + _fileSize = 0; +} + +bool CInOutTempBuffer::WriteToFile(const void *data, UInt32 size) +{ + if (size == 0) + return true; + if(!_tmpFileCreated) + { + CSysString tempDirPath; + if(!MyGetTempPath(tempDirPath)) + return false; + if (_tempFile.Create(tempDirPath, kTempFilePrefixString, _tmpFileName) == 0) + return false; + // _outFile.SetOpenCreationDispositionCreateAlways(); + if(!_outFile.Create(_tmpFileName, true)) + return false; + _tmpFileCreated = true; + } + UInt32 processedSize; + if(!_outFile.Write(data, size, processedSize)) + return false; + _fileSize += processedSize; + return (processedSize == size); +} + +bool CInOutTempBuffer::FlushWrite() +{ + return _outFile.Close(); +} + +bool CInOutTempBuffer::Write(const void *data, UInt32 size) +{ + if(_bufferPosition < kTmpBufferMemorySize) + { + UInt32 curSize = MyMin(kTmpBufferMemorySize - _bufferPosition, size); + memmove(_buffer + _bufferPosition, (const Byte *)data, curSize); + _bufferPosition += curSize; + size -= curSize; + data = ((const Byte *)data) + curSize; + _fileSize += curSize; + } + return WriteToFile(data, size); +} + +bool CInOutTempBuffer::InitReading() +{ + _currentPositionInBuffer = 0; + if(_tmpFileCreated) + return _inFile.Open(_tmpFileName); + return true; +} + +HRESULT CInOutTempBuffer::WriteToStream(ISequentialOutStream *stream) +{ + if (_currentPositionInBuffer < _bufferPosition) + { + UInt32 sizeToWrite = _bufferPosition - _currentPositionInBuffer; + RINOK(WriteStream(stream, _buffer + _currentPositionInBuffer, sizeToWrite, NULL)); + _currentPositionInBuffer += sizeToWrite; + } + if (!_tmpFileCreated) + return true; + for (;;) + { + UInt32 localProcessedSize; + if (!_inFile.ReadPart(_buffer, kTmpBufferMemorySize, localProcessedSize)) + return E_FAIL; + if (localProcessedSize == 0) + return S_OK; + RINOK(WriteStream(stream, _buffer, localProcessedSize, NULL)); + } +} + +STDMETHODIMP CSequentialOutTempBufferImp::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (!_buffer->Write(data, size)) + { + if (processedSize != NULL) + *processedSize = 0; + return E_FAIL; + } + if (processedSize != NULL) + *processedSize = size; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.h b/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.h new file mode 100644 index 0000000..3abe76e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/InOutTempBuffer.h @@ -0,0 +1,55 @@ +// Util/InOutTempBuffer.h + +#ifndef __IN_OUT_TEMP_BUFFER_H +#define __IN_OUT_TEMP_BUFFER_H + +#include "../../Windows/FileIO.h" +#include "../../Windows/FileDir.h" +#include "../../Common/MyCom.h" + +#include "../IStream.h" + +class CInOutTempBuffer +{ + NWindows::NFile::NDirectory::CTempFile _tempFile; + NWindows::NFile::NIO::COutFile _outFile; + NWindows::NFile::NIO::CInFile _inFile; + Byte *_buffer; + UInt32 _bufferPosition; + UInt32 _currentPositionInBuffer; + CSysString _tmpFileName; + bool _tmpFileCreated; + + UInt64 _fileSize; + + bool WriteToFile(const void *data, UInt32 size); +public: + CInOutTempBuffer(); + ~CInOutTempBuffer(); + void Create(); + + void InitWriting(); + bool Write(const void *data, UInt32 size); + UInt64 GetDataSize() const { return _fileSize; } + bool FlushWrite(); + bool InitReading(); + HRESULT WriteToStream(ISequentialOutStream *stream); +}; + +class CSequentialOutTempBufferImp: + public ISequentialOutStream, + public CMyUnknownImp +{ + CInOutTempBuffer *_buffer; +public: + // CSequentialOutStreamImp(): _size(0) {} + // UInt32 _size; + void Init(CInOutTempBuffer *buffer) { _buffer = buffer; } + // UInt32 GetSize() const { return _size; } + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.cpp new file mode 100644 index 0000000..af72114 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.cpp @@ -0,0 +1,24 @@ +// LimitedStreams.cpp + +#include "StdAfx.h" + +#include "LimitedStreams.h" +#include "../../Common/Defs.h" + +STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize = 0; + UInt32 sizeToRead = (UInt32)MyMin((_size - _pos), (UInt64)size); + HRESULT result = S_OK; + if (sizeToRead > 0) + { + result = _stream->Read(data, sizeToRead, &realProcessedSize); + _pos += realProcessedSize; + if (realProcessedSize == 0) + _wasFinished = true; + } + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.h b/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.h new file mode 100644 index 0000000..ec4a3a7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/LimitedStreams.h @@ -0,0 +1,33 @@ +// LimitedStreams.h + +#ifndef __LIMITEDSTREAMS_H +#define __LIMITEDSTREAMS_H + +#include "../../Common/MyCom.h" +#include "../IStream.h" + +class CLimitedSequentialInStream: + public ISequentialInStream, + public CMyUnknownImp +{ + CMyComPtr _stream; + UInt64 _size; + UInt64 _pos; + bool _wasFinished; +public: + void SetStream(ISequentialInStream *stream) { _stream = stream; } + void Init(UInt64 streamSize) + { + _size = streamSize; + _pos = 0; + _wasFinished = false; + } + + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + UInt64 GetSize() const { return _pos; } + bool WasFinished() const { return _wasFinished; } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.cpp new file mode 100644 index 0000000..36be1ce --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.cpp @@ -0,0 +1,23 @@ +// LockedStream.cpp + +#include "StdAfx.h" + +#include "LockedStream.h" + +HRESULT CLockedInStream::Read(UInt64 startPos, void *data, UInt32 size, + UInt32 *processedSize) +{ + NWindows::NSynchronization::CCriticalSectionLock lock(_criticalSection); + RINOK(_stream->Seek(startPos, STREAM_SEEK_SET, NULL)); + return _stream->Read(data, size, processedSize); +} + +STDMETHODIMP CLockedSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize = 0; + HRESULT result = _lockedInStream->Read(_pos, data, size, &realProcessedSize); + _pos += realProcessedSize; + if (processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.h b/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.h new file mode 100644 index 0000000..1c1e179 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/LockedStream.h @@ -0,0 +1,38 @@ +// LockedStream.h + +#ifndef __LOCKEDSTREAM_H +#define __LOCKEDSTREAM_H + +#include "../../Windows/Synchronization.h" +#include "../../Common/MyCom.h" +#include "../IStream.h" + +class CLockedInStream +{ + CMyComPtr _stream; + NWindows::NSynchronization::CCriticalSection _criticalSection; +public: + void Init(IInStream *stream) + { _stream = stream; } + HRESULT Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize); +}; + +class CLockedSequentialInStreamImp: + public ISequentialInStream, + public CMyUnknownImp +{ + CLockedInStream *_lockedInStream; + UInt64 _pos; +public: + void Init(CLockedInStream *lockedInStream, UInt64 startPos) + { + _lockedInStream = lockedInStream; + _pos = startPos; + } + + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.cpp new file mode 100644 index 0000000..b797b68 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.cpp @@ -0,0 +1,27 @@ +// MethodId.cpp + +#include "StdAfx.h" + +#include "MethodId.h" +#include "../../Common/MyString.h" + +static inline wchar_t GetHex(Byte value) +{ + return (wchar_t)((value < 10) ? ('0' + value) : ('A' + (value - 10))); +} + +UString ConvertMethodIdToString(UInt64 id) +{ + wchar_t s[32]; + int len = 32; + s[--len] = 0; + do + { + s[--len] = GetHex((Byte)id & 0xF); + id >>= 4; + s[--len] = GetHex((Byte)id & 0xF); + id >>= 4; + } + while (id != 0); + return s + len; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.h b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.h new file mode 100644 index 0000000..54ebc9f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodId.h @@ -0,0 +1,10 @@ +// MethodId.h + +#ifndef __7Z_METHOD_ID_H +#define __7Z_METHOD_ID_H + +#include "../../Common/Types.h" + +typedef UInt64 CMethodId; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.cpp new file mode 100644 index 0000000..52f5511 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.cpp @@ -0,0 +1,96 @@ +// MethodProps.cpp + +#include "StdAfx.h" + +#include "MethodProps.h" +#include "../../Common/MyCom.h" + +static UInt64 k_LZMA = 0x030101; +// static UInt64 k_LZMA2 = 0x030102; + +HRESULT SetMethodProperties(const CMethod &method, const UInt64 *inSizeForReduce, IUnknown *coder) +{ + bool tryReduce = false; + UInt32 reducedDictionarySize = 1 << 10; + if (inSizeForReduce != 0 && (method.Id == k_LZMA /* || methodFull.MethodID == k_LZMA2 */)) + { + for (;;) + { + const UInt32 step = (reducedDictionarySize >> 1); + if (reducedDictionarySize >= *inSizeForReduce) + { + tryReduce = true; + break; + } + reducedDictionarySize += step; + if (reducedDictionarySize >= *inSizeForReduce) + { + tryReduce = true; + break; + } + if (reducedDictionarySize >= ((UInt32)3 << 30)) + break; + reducedDictionarySize += step; + } + } + + { + int numProperties = method.Properties.Size(); + CMyComPtr setCoderProperties; + coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); + if (setCoderProperties == NULL) + { + if (numProperties != 0) + return E_INVALIDARG; + } + else + { + CRecordVector propIDs; + NWindows::NCOM::CPropVariant *values = new NWindows::NCOM::CPropVariant[numProperties]; + HRESULT res = S_OK; + try + { + for (int i = 0; i < numProperties; i++) + { + const CProp &prop = method.Properties[i]; + propIDs.Add(prop.Id); + NWindows::NCOM::CPropVariant &value = values[i]; + value = prop.Value; + // if (tryReduce && prop.Id == NCoderPropID::kDictionarySize && value.vt == VT_UI4 && reducedDictionarySize < value.ulVal) + if (tryReduce) + if (prop.Id == NCoderPropID::kDictionarySize) + if (value.vt == VT_UI4) + if (reducedDictionarySize < value.ulVal) + value.ulVal = reducedDictionarySize; + } + CMyComPtr setCoderProperties; + coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); + res = setCoderProperties->SetCoderProperties(&propIDs.Front(), values, numProperties); + } + catch(...) + { + delete []values; + throw; + } + delete []values; + RINOK(res); + } + } + + /* + CMyComPtr writeCoderProperties; + coder->QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); + if (writeCoderProperties != NULL) + { + CSequentialOutStreamImp *outStreamSpec = new CSequentialOutStreamImp; + CMyComPtr outStream(outStreamSpec); + outStreamSpec->Init(); + RINOK(writeCoderProperties->WriteCoderProperties(outStream)); + size_t size = outStreamSpec->GetSize(); + filterProps.SetCapacity(size); + memmove(filterProps, outStreamSpec->GetBuffer(), size); + } + */ + return S_OK; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.h b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.h new file mode 100644 index 0000000..7ffa8e4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/MethodProps.h @@ -0,0 +1,41 @@ +// MethodProps.h + +#ifndef __7Z_METHOD_PROPS_H +#define __7Z_METHOD_PROPS_H + +#include "MethodId.h" + +#include "../../Windows/PropVariant.h" +#include "../../Common/MyVector.h" +#include "../ICoder.h" + +struct CProp +{ + PROPID Id; + NWindows::NCOM::CPropVariant Value; +}; + +struct CMethod +{ + CMethodId Id; + CObjectVector Properties; +}; + +struct CMethodsMode +{ + CObjectVector Methods; + #ifdef COMPRESS_MT + UInt32 NumThreads; + #endif + + CMethodsMode() + #ifdef COMPRESS_MT + : NumThreads(1) + #endif + {} + bool IsEmpty() const { return Methods.IsEmpty() ; } +}; + +HRESULT SetMethodProperties(const CMethod &method, const UInt64 *inSizeForReduce, IUnknown *coder); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.cpp new file mode 100644 index 0000000..997ccae --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.cpp @@ -0,0 +1,35 @@ +// OffsetStream.cpp + +#include "StdAfx.h" + +#include "Common/Defs.h" +#include "OffsetStream.h" + +HRESULT COffsetOutStream::Init(IOutStream *stream, UInt64 offset) +{ + _offset = offset; + _stream = stream; + return _stream->Seek(offset, STREAM_SEEK_SET, NULL); +} + +STDMETHODIMP COffsetOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + return _stream->Write(data, size, processedSize); +} + +STDMETHODIMP COffsetOutStream::Seek(Int64 offset, UInt32 seekOrigin, + UInt64 *newPosition) +{ + UInt64 absoluteNewPosition; + if (seekOrigin == STREAM_SEEK_SET) + offset += _offset; + HRESULT result = _stream->Seek(offset, seekOrigin, &absoluteNewPosition); + if (newPosition != NULL) + *newPosition = absoluteNewPosition - _offset; + return result; +} + +STDMETHODIMP COffsetOutStream::SetSize(Int64 newSize) +{ + return _stream->SetSize(_offset + newSize); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.h b/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.h new file mode 100644 index 0000000..57a055c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/OffsetStream.h @@ -0,0 +1,25 @@ +// OffsetStream.h + +#ifndef __OFFSETSTREAM_H +#define __OFFSETSTREAM_H + +#include "Common/MyCom.h" +#include "../IStream.h" + +class COffsetOutStream: + public IOutStream, + public CMyUnknownImp +{ + UInt64 _offset; + CMyComPtr _stream; +public: + HRESULT Init(IOutStream *stream, UInt64 offset); + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + STDMETHOD(SetSize)(Int64 newSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.cpp new file mode 100644 index 0000000..31d17c5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.cpp @@ -0,0 +1,119 @@ +// OutByte.cpp + +#include "StdAfx.h" + +#include "OutBuffer.h" + +extern "C" +{ + #include "../../../C/Alloc.h" +} + +bool COutBuffer::Create(UInt32 bufferSize) +{ + const UInt32 kMinBlockSize = 1; + if (bufferSize < kMinBlockSize) + bufferSize = kMinBlockSize; + if (_buffer != 0 && _bufferSize == bufferSize) + return true; + Free(); + _bufferSize = bufferSize; + _buffer = (Byte *)::MidAlloc(bufferSize); + return (_buffer != 0); +} + +void COutBuffer::Free() +{ + ::MidFree(_buffer); + _buffer = 0; +} + +void COutBuffer::SetStream(ISequentialOutStream *stream) +{ + _stream = stream; +} + +void COutBuffer::Init() +{ + _streamPos = 0; + _limitPos = _bufferSize; + _pos = 0; + _processedSize = 0; + _overDict = false; + #ifdef _NO_EXCEPTIONS + ErrorCode = S_OK; + #endif +} + +UInt64 COutBuffer::GetProcessedSize() const +{ + UInt64 res = _processedSize + _pos - _streamPos; + if (_streamPos > _pos) + res += _bufferSize; + return res; +} + + +HRESULT COutBuffer::FlushPart() +{ + // _streamPos < _bufferSize + UInt32 size = (_streamPos >= _pos) ? (_bufferSize - _streamPos) : (_pos - _streamPos); + HRESULT result = S_OK; + #ifdef _NO_EXCEPTIONS + result = ErrorCode; + #endif + if (_buffer2 != 0) + { + memmove(_buffer2, _buffer + _streamPos, size); + _buffer2 += size; + } + + if (_stream != 0 + #ifdef _NO_EXCEPTIONS + && (ErrorCode == S_OK) + #endif + ) + { + UInt32 processedSize = 0; + result = _stream->Write(_buffer + _streamPos, size, &processedSize); + size = processedSize; + } + _streamPos += size; + if (_streamPos == _bufferSize) + _streamPos = 0; + if (_pos == _bufferSize) + { + _overDict = true; + _pos = 0; + } + _limitPos = (_streamPos > _pos) ? _streamPos : _bufferSize; + _processedSize += size; + return result; +} + +HRESULT COutBuffer::Flush() +{ + #ifdef _NO_EXCEPTIONS + if (ErrorCode != S_OK) + return ErrorCode; + #endif + + while(_streamPos != _pos) + { + HRESULT result = FlushPart(); + if (result != S_OK) + return result; + } + return S_OK; +} + +void COutBuffer::FlushWithCheck() +{ + HRESULT result = Flush(); + #ifdef _NO_EXCEPTIONS + ErrorCode = result; + #else + if (result != S_OK) + throw COutBufferException(result); + #endif +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.h b/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.h new file mode 100644 index 0000000..e99dbf2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/OutBuffer.h @@ -0,0 +1,64 @@ +// OutBuffer.h + +#ifndef __OUTBUFFER_H +#define __OUTBUFFER_H + +#include "../IStream.h" +#include "../../Common/MyCom.h" +#include "../../Common/MyException.h" + +#ifndef _NO_EXCEPTIONS +struct COutBufferException: public CSystemException +{ + COutBufferException(HRESULT errorCode): CSystemException(errorCode) {} +}; +#endif + +class COutBuffer +{ +protected: + Byte *_buffer; + UInt32 _pos; + UInt32 _limitPos; + UInt32 _streamPos; + UInt32 _bufferSize; + CMyComPtr _stream; + UInt64 _processedSize; + Byte *_buffer2; + bool _overDict; + + HRESULT FlushPart(); +public: + #ifdef _NO_EXCEPTIONS + HRESULT ErrorCode; + #endif + + COutBuffer(): _buffer(0), _pos(0), _stream(0), _buffer2(0) {} + ~COutBuffer() { Free(); } + + bool Create(UInt32 bufferSize); + void Free(); + + void SetMemStream(Byte *buffer) { _buffer2 = buffer; } + void SetStream(ISequentialOutStream *stream); + void Init(); + HRESULT Flush(); + void FlushWithCheck(); + void ReleaseStream() { _stream.Release(); } + + void WriteByte(Byte b) + { + _buffer[_pos++] = b; + if(_pos == _limitPos) + FlushWithCheck(); + } + void WriteBytes(const void *data, size_t size) + { + for (size_t i = 0; i < size; i++) + WriteByte(((const Byte *)data)[i]); + } + + UInt64 GetProcessedSize() const; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.cpp new file mode 100644 index 0000000..f24ff6b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.cpp @@ -0,0 +1,42 @@ +// ProgressUtils.h + +#include "StdAfx.h" + +#include "ProgressUtils.h" + +CLocalProgress::CLocalProgress() +{ + ProgressOffset = InSize = OutSize = 0; + SendRatio = SendProgress = true; +} + +void CLocalProgress::Init(IProgress *progress, bool inSizeIsMain) +{ + _ratioProgress.Release(); + _progress = progress; + _progress.QueryInterface(IID_ICompressProgressInfo, &_ratioProgress); + _inSizeIsMain = inSizeIsMain; +} + +STDMETHODIMP CLocalProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + UInt64 inSizeNew = InSize, outSizeNew = OutSize; + if (inSize) + inSizeNew += (*inSize); + if (outSize) + outSizeNew += (*outSize); + if (SendRatio && _ratioProgress) + { + RINOK(_ratioProgress->SetRatioInfo(&inSizeNew, &outSizeNew)); + } + inSizeNew += ProgressOffset; + outSizeNew += ProgressOffset; + if (SendProgress) + return _progress->SetCompleted(_inSizeIsMain ? &inSizeNew : &outSizeNew); + return S_OK; +} + +HRESULT CLocalProgress::SetCur() +{ + return SetRatioInfo(NULL, NULL); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.h b/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.h new file mode 100644 index 0000000..831c736 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/ProgressUtils.h @@ -0,0 +1,34 @@ +// ProgressUtils.h + +#ifndef __PROGRESSUTILS_H +#define __PROGRESSUTILS_H + +#include "../../Common/MyCom.h" + +#include "../ICoder.h" +#include "../IProgress.h" + +class CLocalProgress: + public ICompressProgressInfo, + public CMyUnknownImp +{ + CMyComPtr _progress; + CMyComPtr _ratioProgress; + bool _inSizeIsMain; +public: + UInt64 ProgressOffset; + UInt64 InSize; + UInt64 OutSize; + bool SendRatio; + bool SendProgress; + + CLocalProgress(); + void Init(IProgress *progress, bool inSizeIsMain); + HRESULT SetCur(); + + MY_UNKNOWN_IMP + + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterArc.h b/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterArc.h new file mode 100644 index 0000000..5773021 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterArc.h @@ -0,0 +1,36 @@ +// RegisterArc.h + +#ifndef __REGISTERARC_H +#define __REGISTERARC_H + +#include "../Archive/IArchive.h" + +typedef IInArchive * (*CreateInArchiveP)(); +typedef IOutArchive * (*CreateOutArchiveP)(); + +struct CArcInfo +{ + const wchar_t *Name; + const wchar_t *Ext; + const wchar_t *AddExt; + Byte ClassId; + Byte Signature[16]; + int SignatureSize; + bool KeepName; + CreateInArchiveP CreateInArchive; + CreateOutArchiveP CreateOutArchive; +}; + +void RegisterArc(const CArcInfo *arcInfo); + +#define REGISTER_ARC_NAME(x) CRegister ## x + +#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \ + REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; + +#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \ + REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterCodec.h b/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterCodec.h new file mode 100644 index 0000000..1f1cd4d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/RegisterCodec.h @@ -0,0 +1,33 @@ +// RegisterCodec.h + +#ifndef __REGISTERCODEC_H +#define __REGISTERCODEC_H + +#include "../Common/MethodId.h" + +typedef void * (*CreateCodecP)(); +struct CCodecInfo +{ + CreateCodecP CreateDecoder; + CreateCodecP CreateEncoder; + CMethodId Id; + const wchar_t *Name; + UInt32 NumInStreams; + bool IsFilter; +}; + +void RegisterCodec(const CCodecInfo *codecInfo); + +#define REGISTER_CODEC_NAME(x) CRegisterCodec ## x + +#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \ + REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \ + static REGISTER_CODEC_NAME(x) g_RegisterCodec; + +#define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x +#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \ + REGISTER_CODECS_NAME(x)() { for (int i = 0; i < sizeof(g_CodecsInfo) / sizeof(g_CodecsInfo[0]); i++) \ + RegisterCodec(&g_CodecsInfo[i]); }}; \ + static REGISTER_CODECS_NAME(x) g_RegisterCodecs; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Common/StdAfx.h new file mode 100644 index 0000000..27a77b1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../Common/MyWindows.h" +#include "../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.cpp new file mode 100644 index 0000000..5db9d01 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.cpp @@ -0,0 +1,150 @@ +// StreamBinder.cpp + +#include "StdAfx.h" + +#include "StreamBinder.h" +#include "../../Common/Defs.h" +#include "../../Common/MyCom.h" + +using namespace NWindows; +using namespace NSynchronization; + +class CSequentialInStreamForBinder: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +private: + CStreamBinder *m_StreamBinder; +public: + ~CSequentialInStreamForBinder() { m_StreamBinder->CloseRead(); } + void SetBinder(CStreamBinder *streamBinder) { m_StreamBinder = streamBinder; } +}; + +STDMETHODIMP CSequentialInStreamForBinder::Read(void *data, UInt32 size, UInt32 *processedSize) + { return m_StreamBinder->Read(data, size, processedSize); } + +class CSequentialOutStreamForBinder: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + +private: + CStreamBinder *m_StreamBinder; +public: + ~CSequentialOutStreamForBinder() { m_StreamBinder->CloseWrite(); } + void SetBinder(CStreamBinder *streamBinder) { m_StreamBinder = streamBinder; } +}; + +STDMETHODIMP CSequentialOutStreamForBinder::Write(const void *data, UInt32 size, UInt32 *processedSize) + { return m_StreamBinder->Write(data, size, processedSize); } + + +////////////////////////// +// CStreamBinder +// (_thereAreBytesToReadEvent && _bufferSize == 0) means that stream is finished. + +HRes CStreamBinder::CreateEvents() +{ + RINOK(_allBytesAreWritenEvent.Create(true)); + RINOK(_thereAreBytesToReadEvent.Create()); + return _readStreamIsClosedEvent.Create(); +} + +void CStreamBinder::ReInit() +{ + _thereAreBytesToReadEvent.Reset(); + _readStreamIsClosedEvent.Reset(); + ProcessedSize = 0; +} + + + +void CStreamBinder::CreateStreams(ISequentialInStream **inStream, + ISequentialOutStream **outStream) +{ + CSequentialInStreamForBinder *inStreamSpec = new + CSequentialInStreamForBinder; + CMyComPtr inStreamLoc(inStreamSpec); + inStreamSpec->SetBinder(this); + *inStream = inStreamLoc.Detach(); + + CSequentialOutStreamForBinder *outStreamSpec = new + CSequentialOutStreamForBinder; + CMyComPtr outStreamLoc(outStreamSpec); + outStreamSpec->SetBinder(this); + *outStream = outStreamLoc.Detach(); + + _buffer = NULL; + _bufferSize= 0; + ProcessedSize = 0; +} + +HRESULT CStreamBinder::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 sizeToRead = size; + if (size > 0) + { + RINOK(_thereAreBytesToReadEvent.Lock()); + sizeToRead = MyMin(_bufferSize, size); + if (_bufferSize > 0) + { + MoveMemory(data, _buffer, sizeToRead); + _buffer = ((const Byte *)_buffer) + sizeToRead; + _bufferSize -= sizeToRead; + if (_bufferSize == 0) + { + _thereAreBytesToReadEvent.Reset(); + _allBytesAreWritenEvent.Set(); + } + } + } + if (processedSize != NULL) + *processedSize = sizeToRead; + ProcessedSize += sizeToRead; + return S_OK; +} + +void CStreamBinder::CloseRead() +{ + _readStreamIsClosedEvent.Set(); +} + +HRESULT CStreamBinder::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (size > 0) + { + _buffer = data; + _bufferSize = size; + _allBytesAreWritenEvent.Reset(); + _thereAreBytesToReadEvent.Set(); + + HANDLE events[2]; + events[0] = _allBytesAreWritenEvent; + events[1] = _readStreamIsClosedEvent; + DWORD waitResult = ::WaitForMultipleObjects(2, events, FALSE, INFINITE); + if (waitResult != WAIT_OBJECT_0 + 0) + { + // ReadingWasClosed = true; + return S_FALSE; + } + // if(!_allBytesAreWritenEvent.Lock()) + // return E_FAIL; + } + if (processedSize != NULL) + *processedSize = size; + return S_OK; +} + +void CStreamBinder::CloseWrite() +{ + // _bufferSize must be = 0 + _thereAreBytesToReadEvent.Set(); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.h b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.h new file mode 100644 index 0000000..1de2372 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamBinder.h @@ -0,0 +1,32 @@ +// StreamBinder.h + +#ifndef __STREAMBINDER_H +#define __STREAMBINDER_H + +#include "../IStream.h" +#include "../../Windows/Synchronization.h" + +class CStreamBinder +{ + NWindows::NSynchronization::CManualResetEvent _allBytesAreWritenEvent; + NWindows::NSynchronization::CManualResetEvent _thereAreBytesToReadEvent; + NWindows::NSynchronization::CManualResetEvent _readStreamIsClosedEvent; + UInt32 _bufferSize; + const void *_buffer; +public: + // bool ReadingWasClosed; + UInt64 ProcessedSize; + CStreamBinder() {} + HRes CreateEvents(); + + void CreateStreams(ISequentialInStream **inStream, + ISequentialOutStream **outStream); + HRESULT Read(void *data, UInt32 size, UInt32 *processedSize); + void CloseRead(); + + HRESULT Write(const void *data, UInt32 size, UInt32 *processedSize); + void CloseWrite(); + void ReInit(); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.cpp new file mode 100644 index 0000000..32f8f30 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.cpp @@ -0,0 +1,68 @@ +// StreamObjects.cpp + +#include "StdAfx.h" + +#include "StreamObjects.h" +#include "../../Common/Defs.h" + + +STDMETHODIMP CSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 numBytesToRead = (UInt32)(MyMin(_pos + size, _size) - _pos); + memmove(data, _dataPointer + _pos, numBytesToRead); + _pos += numBytesToRead; + if(processedSize != NULL) + *processedSize = numBytesToRead; + return S_OK; +} + + +void CWriteBuffer::Write(const void *data, size_t size) +{ + size_t newCapacity = _size + size; + _buffer.EnsureCapacity(newCapacity); + memmove(_buffer + _size, data, size); + _size += size; +} + +STDMETHODIMP CSequentialOutStreamImp::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + _writeBuffer.Write(data, size); + if(processedSize != NULL) + *processedSize = size; + return S_OK; +} + +STDMETHODIMP CSequentialOutStreamImp2::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 newSize = size; + if (_pos + size > _size) + newSize = (UInt32)(_size - _pos); + memmove(_buffer + _pos, data, newSize); + if(processedSize != NULL) + *processedSize = newSize; + _pos += newSize; + if (newSize != size) + return E_FAIL; + return S_OK; +} + +STDMETHODIMP CSequentialInStreamSizeCount::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + if (processedSize != 0) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CSequentialOutStreamSizeCount::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Write(data, size, &realProcessedSize); + _size += realProcessedSize; + if (processedSize != 0) + *processedSize = realProcessedSize; + return result; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.h b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.h new file mode 100644 index 0000000..6f670f5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamObjects.h @@ -0,0 +1,117 @@ +// StreamObjects.h + +#ifndef __STREAMOBJECTS_H +#define __STREAMOBJECTS_H + +#include "../../Common/DynamicBuffer.h" +#include "../../Common/MyCom.h" +#include "../IStream.h" + +class CSequentialInStreamImp: + public ISequentialInStream, + public CMyUnknownImp +{ + const Byte *_dataPointer; + size_t _size; + size_t _pos; + +public: + void Init(const Byte *dataPointer, size_t size) + { + _dataPointer = dataPointer; + _size = size; + _pos = 0; + } + + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + +class CWriteBuffer +{ + CByteDynamicBuffer _buffer; + size_t _size; +public: + CWriteBuffer(): _size(0) {} + void Init() { _size = 0; } + void Write(const void *data, size_t size); + size_t GetSize() const { return _size; } + const CByteDynamicBuffer& GetBuffer() const { return _buffer; } +}; + +class CSequentialOutStreamImp: + public ISequentialOutStream, + public CMyUnknownImp +{ + CWriteBuffer _writeBuffer; +public: + void Init() { _writeBuffer.Init(); } + size_t GetSize() const { return _writeBuffer.GetSize(); } + const CByteDynamicBuffer& GetBuffer() const { return _writeBuffer.GetBuffer(); } + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +class CSequentialOutStreamImp2: + public ISequentialOutStream, + public CMyUnknownImp +{ + Byte *_buffer; + size_t _size; + size_t _pos; +public: + + void Init(Byte *buffer, size_t size) + { + _buffer = buffer; + _pos = 0; + _size = size; + } + + size_t GetPos() const { return _pos; } + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +class CSequentialInStreamSizeCount: + public ISequentialInStream, + public CMyUnknownImp +{ + CMyComPtr _stream; + UInt64 _size; +public: + void Init(ISequentialInStream *stream) + { + _stream = stream; + _size = 0; + } + UInt64 GetSize() const { return _size; } + + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + +class CSequentialOutStreamSizeCount: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr _stream; + UInt64 _size; +public: + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void Init() { _size = 0; } + UInt64 GetSize() const { return _size; } + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.cpp new file mode 100644 index 0000000..1d95127 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.cpp @@ -0,0 +1,44 @@ +// StreamUtils.cpp + +#include "StdAfx.h" + +#include "../../Common/MyCom.h" +#include "StreamUtils.h" + +HRESULT ReadStream(ISequentialInStream *stream, void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != 0) + *processedSize = 0; + while(size != 0) + { + UInt32 processedSizeLoc; + HRESULT res = stream->Read(data, size, &processedSizeLoc); + if (processedSize != 0) + *processedSize += processedSizeLoc; + data = (Byte *)((Byte *)data + processedSizeLoc); + size -= processedSizeLoc; + RINOK(res); + if (processedSizeLoc == 0) + return S_OK; + } + return S_OK; +} + +HRESULT WriteStream(ISequentialOutStream *stream, const void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != 0) + *processedSize = 0; + while(size != 0) + { + UInt32 processedSizeLoc; + HRESULT res = stream->Write(data, size, &processedSizeLoc); + if (processedSize != 0) + *processedSize += processedSizeLoc; + data = (const void *)((const Byte *)data + processedSizeLoc); + size -= processedSizeLoc; + RINOK(res); + if (processedSizeLoc == 0) + return E_FAIL; + } + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.h b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.h new file mode 100644 index 0000000..59f8873 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/StreamUtils.h @@ -0,0 +1,11 @@ +// StreamUtils.h + +#ifndef __STREAMUTILS_H +#define __STREAMUTILS_H + +#include "../IStream.h" + +HRESULT ReadStream(ISequentialInStream *stream, void *data, UInt32 size, UInt32 *processedSize); +HRESULT WriteStream(ISequentialOutStream *stream, const void *data, UInt32 size, UInt32 *processedSize); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.cpp b/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.cpp new file mode 100644 index 0000000..3567f98 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.cpp @@ -0,0 +1,45 @@ +// VirtThread.cpp + +#include "StdAfx.h" + +#include "VirtThread.h" + +static THREAD_FUNC_DECL CoderThread(void *p) +{ + for (;;) + { + CVirtThread *t = (CVirtThread *)p; + t->StartEvent.Lock(); + if (t->ExitEvent) + return 0; + t->Execute(); + t->FinishedEvent.Set(); + } +} + +HRes CVirtThread::Create() +{ + RINOK(StartEvent.CreateIfNotCreated()); + RINOK(FinishedEvent.CreateIfNotCreated()); + StartEvent.Reset(); + FinishedEvent.Reset(); + ExitEvent = false; + if (Thread.IsCreated()) + return S_OK; + return Thread.Create(CoderThread, this); +} + +void CVirtThread::Start() +{ + ExitEvent = false; + StartEvent.Set(); +} + +CVirtThread::~CVirtThread() +{ + ExitEvent = true; + if (StartEvent.IsCreated()) + StartEvent.Set(); + Thread.Wait(); +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.h b/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.h new file mode 100644 index 0000000..62c055b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Common/VirtThread.h @@ -0,0 +1,23 @@ +// VirtThread.h + +#ifndef __VIRTTHREAD_H +#define __VIRTTHREAD_H + +#include "../../Windows/Synchronization.h" +#include "../../Windows/Thread.h" + +struct CVirtThread +{ + NWindows::NSynchronization::CAutoResetEvent StartEvent; + NWindows::NSynchronization::CAutoResetEvent FinishedEvent; + NWindows::CThread Thread; + bool ExitEvent; + + ~CVirtThread(); + HRes Create(); + void Start(); + void WaitFinish() { FinishedEvent.Lock(); } + virtual void Execute() = 0; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.cpp new file mode 100644 index 0000000..5870bc0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.cpp @@ -0,0 +1,19 @@ +// ARM.cpp + +#include "StdAfx.h" +#include "ARM.h" + +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchARM.h" +} + +UInt32 CBC_ARM_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return ::ARM_Convert(data, size, _bufferPos, 1); +} + +UInt32 CBC_ARM_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return ::ARM_Convert(data, size, _bufferPos, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.h new file mode 100644 index 0000000..5561299 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARM.h @@ -0,0 +1,10 @@ +// ARM.h + +#ifndef __ARM_H +#define __ARM_H + +#include "BranchCoder.h" + +MyClassA(BC_ARM, 0x05, 1) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.cpp new file mode 100644 index 0000000..7df641a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.cpp @@ -0,0 +1,20 @@ +// ARMThumb.cpp + +#include "StdAfx.h" + +#include "ARMThumb.h" + +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchARMThumb.h" +} + +UInt32 CBC_ARMThumb_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return ::ARMThumb_Convert(data, size, _bufferPos, 1); +} + +UInt32 CBC_ARMThumb_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return ::ARMThumb_Convert(data, size, _bufferPos, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.h new file mode 100644 index 0000000..601e40b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.h @@ -0,0 +1,10 @@ +// ARMThumb.h + +#ifndef __ARMTHUMB_H +#define __ARMTHUMB_H + +#include "BranchCoder.h" + +MyClassA(BC_ARMThumb, 0x07, 1) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJ2Register.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJ2Register.cpp new file mode 100644 index 0000000..88ebbac --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJ2Register.cpp @@ -0,0 +1,18 @@ +// BranchRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "x86_2.h" +static void *CreateCodec() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CDecoder()); } +#ifndef EXTRACT_ONLY +static void *CreateCodecOut() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CEncoder()); } +#else +#define CreateCodecOut 0 +#endif + +static CCodecInfo g_CodecInfo = + { CreateCodec, CreateCodecOut, 0x0303011B, L"BCJ2", 4, false }; + +REGISTER_CODEC(BCJ2) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJRegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJRegister.cpp new file mode 100644 index 0000000..a62d964 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BCJRegister.cpp @@ -0,0 +1,18 @@ +// BranchRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "x86.h" +static void *CreateCodec() { return (void *)(ICompressFilter *)(new CBCJ_x86_Decoder()); } +#ifndef EXTRACT_ONLY +static void *CreateCodecOut() { return (void *)(ICompressFilter *)(new CBCJ_x86_Encoder()); } +#else +#define CreateCodecOut 0 +#endif + +static CCodecInfo g_CodecInfo = + { CreateCodec, CreateCodecOut, 0x03030103, L"BCJ", 1, true }; + +REGISTER_CODEC(BCJ) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.cpp new file mode 100644 index 0000000..8d25f0d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.cpp @@ -0,0 +1,18 @@ +// BranchCoder.cpp + +#include "StdAfx.h" +#include "BranchCoder.h" + +STDMETHODIMP CBranchConverter::Init() +{ + _bufferPos = 0; + SubInit(); + return S_OK; +} + +STDMETHODIMP_(UInt32) CBranchConverter::Filter(Byte *data, UInt32 size) +{ + UInt32 processedSize = SubFilter(data, size); + _bufferPos += processedSize; + return processedSize; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.h new file mode 100644 index 0000000..102f0da --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.h @@ -0,0 +1,45 @@ +// BranchCoder.h + +#ifndef __BRANCH_CODER_H +#define __BRANCH_CODER_H + +#include "Common/MyCom.h" +#include "Common/Types.h" + +#include "../../ICoder.h" + +class CBranchConverter: + public ICompressFilter, + public CMyUnknownImp +{ +protected: + UInt32 _bufferPos; + virtual void SubInit() {} + virtual UInt32 SubFilter(Byte *data, UInt32 size) = 0; +public: + MY_UNKNOWN_IMP; + STDMETHOD(Init)(); + STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); +}; + +#define MyClassEncoderA(Name) class C ## Name: public CBranchConverter \ + { public: UInt32 SubFilter(Byte *data, UInt32 size); }; + +#define MyClassDecoderA(Name) class C ## Name: public CBranchConverter \ + { public: UInt32 SubFilter(Byte *data, UInt32 size); }; + +#define MyClassEncoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ + { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; + +#define MyClassDecoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ + { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; + +#define MyClassA(Name, id, subId) \ +MyClassEncoderA(Name ## _Encoder) \ +MyClassDecoderA(Name ## _Decoder) + +#define MyClassB(Name, id, subId, ADD_ITEMS, ADD_INIT) \ +MyClassEncoderB(Name ## _Encoder, ADD_ITEMS, ADD_INIT) \ +MyClassDecoderB(Name ## _Decoder, ADD_ITEMS, ADD_INIT) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchRegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchRegister.cpp new file mode 100644 index 0000000..2ccdcc6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/BranchRegister.cpp @@ -0,0 +1,34 @@ +// BranchRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "PPC.h" +#include "IA64.h" +#include "ARM.h" +#include "ARMThumb.h" +#include "SPARC.h" + +#define CREATE_CODEC(x) \ + static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## _Decoder); } \ + static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## _Encoder); } + +CREATE_CODEC(BC_PPC_B) +CREATE_CODEC(BC_IA64) +CREATE_CODEC(BC_ARM) +CREATE_CODEC(BC_ARMThumb) +CREATE_CODEC(BC_SPARC) + +#define METHOD_ITEM(x, id1, id2, name) { CreateCodec ## x, CreateCodec ## x ## Out, 0x03030000 + (id1 * 256) + id2, name, 1, true } + +static CCodecInfo g_CodecsInfo[] = +{ + METHOD_ITEM(BC_PPC_B, 0x02, 0x05, L"BC_PPC_B"), + METHOD_ITEM(BC_IA64, 0x04, 1, L"BC_IA64"), + METHOD_ITEM(BC_ARM, 0x05, 1, L"BC_ARM"), + METHOD_ITEM(BC_ARMThumb,0x07, 1, L"BC_ARMThumb"), + METHOD_ITEM(BC_SPARC, 0x08, 0x05, L"BC_SPARC") +}; + +REGISTER_CODECS(Branch) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.cpp new file mode 100644 index 0000000..ae4766a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.cpp @@ -0,0 +1,19 @@ +// IA64.cpp + +#include "StdAfx.h" +#include "IA64.h" + +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchIA64.h" +} + +UInt32 CBC_IA64_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return ::IA64_Convert(data, size, _bufferPos, 1); +} + +UInt32 CBC_IA64_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return ::IA64_Convert(data, size, _bufferPos, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.h new file mode 100644 index 0000000..7fe715e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/IA64.h @@ -0,0 +1,10 @@ +// IA64.h + +#ifndef __IA64_H +#define __IA64_H + +#include "BranchCoder.h" + +MyClassA(BC_IA64, 0x04, 1) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.cpp new file mode 100644 index 0000000..ecd4b3d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.cpp @@ -0,0 +1,19 @@ +// PPC.cpp + +#include "StdAfx.h" +#include "PPC.h" + +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchPPC.h" +} + +UInt32 CBC_PPC_B_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return ::PPC_B_Convert(data, size, _bufferPos, 1); +} + +UInt32 CBC_PPC_B_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return ::PPC_B_Convert(data, size, _bufferPos, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.h new file mode 100644 index 0000000..a0e3344 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/PPC.h @@ -0,0 +1,10 @@ +// PPC.h + +#ifndef __PPC_H +#define __PPC_H + +#include "BranchCoder.h" + +MyClassA(BC_PPC_B, 0x02, 5) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.cpp new file mode 100644 index 0000000..5678eb3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.cpp @@ -0,0 +1,19 @@ +// SPARC.cpp + +#include "StdAfx.h" +#include "SPARC.h" + +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchSPARC.h" +} + +UInt32 CBC_SPARC_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return ::SPARC_Convert(data, size, _bufferPos, 1); +} + +UInt32 CBC_SPARC_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return ::SPARC_Convert(data, size, _bufferPos, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.h new file mode 100644 index 0000000..e0a682e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/SPARC.h @@ -0,0 +1,10 @@ +// SPARC.h + +#ifndef __SPARC_H +#define __SPARC_H + +#include "BranchCoder.h" + +MyClassA(BC_SPARC, 0x08, 5) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.h new file mode 100644 index 0000000..e7fb698 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.cpp new file mode 100644 index 0000000..79d4965 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.cpp @@ -0,0 +1,14 @@ +// x86.cpp + +#include "StdAfx.h" +#include "x86.h" + +UInt32 CBCJ_x86_Encoder::SubFilter(Byte *data, UInt32 size) +{ + return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 1); +} + +UInt32 CBCJ_x86_Decoder::SubFilter(Byte *data, UInt32 size) +{ + return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 0); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.h new file mode 100644 index 0000000..5817660 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86.h @@ -0,0 +1,21 @@ +// x86.h + +#ifndef __X86_H +#define __X86_H + +#include "BranchCoder.h" +extern "C" +{ +#include "../../../../C/Compress/Branch/BranchX86.h" +} + +struct CBranch86 +{ + UInt32 _prevMask; + void x86Init() { x86_Convert_Init(_prevMask); } +}; + +MyClassB(BCJ_x86, 0x01, 3, CBranch86 , + virtual void SubInit() { x86Init(); }) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.cpp new file mode 100644 index 0000000..51176c9 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.cpp @@ -0,0 +1,392 @@ +// x86_2.cpp + +#include "StdAfx.h" +#include "x86_2.h" + +extern "C" +{ +#include "../../../../C/Alloc.h" +} + +namespace NCompress { +namespace NBcj2 { + +inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); } +inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); } +inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); } + +#ifndef EXTRACT_ONLY + +static const int kBufferSize = 1 << 17; + +static bool inline Test86MSByte(Byte b) +{ + return (b == 0 || b == 0xFF); +} + +bool CEncoder::Create() +{ + if (!_mainStream.Create(1 << 16)) + return false; + if (!_callStream.Create(1 << 20)) + return false; + if (!_jumpStream.Create(1 << 20)) + return false; + if (!_rangeEncoder.Create(1 << 20)) + return false; + if (_buffer == 0) + { + _buffer = (Byte *)MidAlloc(kBufferSize); + if (_buffer == 0) + return false; + } + return true; +} + +CEncoder::~CEncoder() +{ + ::MidFree(_buffer); +} + +HRESULT CEncoder::Flush() +{ + RINOK(_mainStream.Flush()); + RINOK(_callStream.Flush()); + RINOK(_jumpStream.Flush()); + _rangeEncoder.FlushData(); + return _rangeEncoder.FlushStream(); +} + +const UInt32 kDefaultLimit = (1 << 24); + +HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 ** /* outSizes */, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + if (numInStreams != 1 || numOutStreams != 4) + return E_INVALIDARG; + + if (!Create()) + return E_OUTOFMEMORY; + + bool sizeIsDefined = false; + UInt64 inSize = 0; + if (inSizes != NULL) + if (inSizes[0] != NULL) + { + inSize = *inSizes[0]; + if (inSize <= kDefaultLimit) + sizeIsDefined = true; + } + + ISequentialInStream *inStream = inStreams[0]; + + _mainStream.SetStream(outStreams[0]); + _mainStream.Init(); + _callStream.SetStream(outStreams[1]); + _callStream.Init(); + _jumpStream.SetStream(outStreams[2]); + _jumpStream.Init(); + _rangeEncoder.SetStream(outStreams[3]); + _rangeEncoder.Init(); + for (int i = 0; i < 256 + 2; i++) + _statusEncoder[i].Init(); + CCoderReleaser releaser(this); + + CMyComPtr getSubStreamSize; + { + inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize); + } + + UInt32 nowPos = 0; + UInt64 nowPos64 = 0; + UInt32 bufferPos = 0; + + Byte prevByte = 0; + + UInt64 subStreamIndex = 0; + UInt64 subStreamStartPos = 0; + UInt64 subStreamEndPos = 0; + + for (;;) + { + UInt32 processedSize = 0; + for (;;) + { + UInt32 size = kBufferSize - (bufferPos + processedSize); + UInt32 processedSizeLoc; + if (size == 0) + break; + RINOK(inStream->Read(_buffer + bufferPos + processedSize, size, &processedSizeLoc)); + if (processedSizeLoc == 0) + break; + processedSize += processedSizeLoc; + } + UInt32 endPos = bufferPos + processedSize; + + if (endPos < 5) + { + // change it + for (bufferPos = 0; bufferPos < endPos; bufferPos++) + { + Byte b = _buffer[bufferPos]; + _mainStream.WriteByte(b); + UInt32 index; + if (b == 0xE8) + index = prevByte; + else if (b == 0xE9) + index = 256; + else if (IsJcc(prevByte, b)) + index = 257; + else + { + prevByte = b; + continue; + } + _statusEncoder[index].Encode(&_rangeEncoder, 0); + prevByte = b; + } + return Flush(); + } + + bufferPos = 0; + + UInt32 limit = endPos - 5; + while(bufferPos <= limit) + { + Byte b = _buffer[bufferPos]; + _mainStream.WriteByte(b); + if (!IsJ(prevByte, b)) + { + bufferPos++; + prevByte = b; + continue; + } + Byte nextByte = _buffer[bufferPos + 4]; + UInt32 src = + (UInt32(nextByte) << 24) | + (UInt32(_buffer[bufferPos + 3]) << 16) | + (UInt32(_buffer[bufferPos + 2]) << 8) | + (_buffer[bufferPos + 1]); + UInt32 dest = (nowPos + bufferPos + 5) + src; + // if (Test86MSByte(nextByte)) + bool convert; + if (getSubStreamSize != NULL) + { + UInt64 currentPos = (nowPos64 + bufferPos); + while (subStreamEndPos < currentPos) + { + UInt64 subStreamSize; + HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize); + if (result == S_OK) + { + subStreamStartPos = subStreamEndPos; + subStreamEndPos += subStreamSize; + subStreamIndex++; + } + else if (result == S_FALSE || result == E_NOTIMPL) + { + getSubStreamSize.Release(); + subStreamStartPos = 0; + subStreamEndPos = subStreamStartPos - 1; + } + else + return result; + } + if (getSubStreamSize == NULL) + { + if (sizeIsDefined) + convert = (dest < inSize); + else + convert = Test86MSByte(nextByte); + } + else if (subStreamEndPos - subStreamStartPos > kDefaultLimit) + convert = Test86MSByte(nextByte); + else + { + UInt64 dest64 = (currentPos + 5) + Int64(Int32(src)); + convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos); + } + } + else if (sizeIsDefined) + convert = (dest < inSize); + else + convert = Test86MSByte(nextByte); + unsigned index = GetIndex(prevByte, b); + if (convert) + { + _statusEncoder[index].Encode(&_rangeEncoder, 1); + bufferPos += 5; + COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; + for (int i = 24; i >= 0; i -= 8) + s.WriteByte((Byte)(dest >> i)); + prevByte = nextByte; + } + else + { + _statusEncoder[index].Encode(&_rangeEncoder, 0); + bufferPos++; + prevByte = b; + } + } + nowPos += bufferPos; + nowPos64 += bufferPos; + + if (progress != NULL) + { + /* + const UInt64 compressedSize = + _mainStream.GetProcessedSize() + + _callStream.GetProcessedSize() + + _jumpStream.GetProcessedSize() + + _rangeEncoder.GetProcessedSize(); + */ + RINOK(progress->SetRatioInfo(&nowPos64, NULL)); + } + + UInt32 i = 0; + while(bufferPos < endPos) + _buffer[i++] = _buffer[bufferPos++]; + bufferPos = i; + } +} + +STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + try + { + return CodeReal(inStreams, inSizes, numInStreams, + outStreams, outSizes,numOutStreams, progress); + } + catch(const COutBufferException &e) { return e.ErrorCode; } + catch(...) { return S_FALSE; } +} + +#endif + +HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams, + const UInt64 ** /* inSizes */, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 ** /* outSizes */, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + if (numInStreams != 4 || numOutStreams != 1) + return E_INVALIDARG; + + if (!_mainInStream.Create(1 << 16)) + return E_OUTOFMEMORY; + if (!_callStream.Create(1 << 20)) + return E_OUTOFMEMORY; + if (!_jumpStream.Create(1 << 16)) + return E_OUTOFMEMORY; + if (!_rangeDecoder.Create(1 << 20)) + return E_OUTOFMEMORY; + if (!_outStream.Create(1 << 16)) + return E_OUTOFMEMORY; + + _mainInStream.SetStream(inStreams[0]); + _callStream.SetStream(inStreams[1]); + _jumpStream.SetStream(inStreams[2]); + _rangeDecoder.SetStream(inStreams[3]); + _outStream.SetStream(outStreams[0]); + + _mainInStream.Init(); + _callStream.Init(); + _jumpStream.Init(); + _rangeDecoder.Init(); + _outStream.Init(); + + for (int i = 0; i < 256 + 2; i++) + _statusDecoder[i].Init(); + + CCoderReleaser releaser(this); + + Byte prevByte = 0; + UInt32 processedBytes = 0; + for (;;) + { + if (processedBytes >= (1 << 20) && progress != NULL) + { + /* + const UInt64 compressedSize = + _mainInStream.GetProcessedSize() + + _callStream.GetProcessedSize() + + _jumpStream.GetProcessedSize() + + _rangeDecoder.GetProcessedSize(); + */ + const UInt64 nowPos64 = _outStream.GetProcessedSize(); + RINOK(progress->SetRatioInfo(NULL, &nowPos64)); + processedBytes = 0; + } + UInt32 i; + Byte b = 0; + const UInt32 kBurstSize = (1 << 18); + for (i = 0; i < kBurstSize; i++) + { + if (!_mainInStream.ReadByte(b)) + return Flush(); + _outStream.WriteByte(b); + if (IsJ(prevByte, b)) + break; + prevByte = b; + } + processedBytes += i; + if (i == kBurstSize) + continue; + unsigned index = GetIndex(prevByte, b); + if (_statusDecoder[index].Decode(&_rangeDecoder) == 1) + { + UInt32 src = 0; + CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; + for (int i = 0; i < 4; i++) + { + Byte b0; + if(!s.ReadByte(b0)) + return S_FALSE; + src <<= 8; + src |= ((UInt32)b0); + } + UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ; + _outStream.WriteByte((Byte)(dest)); + _outStream.WriteByte((Byte)(dest >> 8)); + _outStream.WriteByte((Byte)(dest >> 16)); + _outStream.WriteByte((Byte)(dest >> 24)); + prevByte = (Byte)(dest >> 24); + processedBytes += 4; + } + else + prevByte = b; + } +} + +STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + try + { + return CodeReal(inStreams, inSizes, numInStreams, + outStreams, outSizes,numOutStreams, progress); + } + catch(const CInBufferException &e) { return e.ErrorCode; } + catch(const COutBufferException &e) { return e.ErrorCode; } + catch(...) { return S_FALSE; } +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.h new file mode 100644 index 0000000..2e4a526 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Branch/x86_2.h @@ -0,0 +1,123 @@ +// x86_2.h + +#ifndef __BRANCH_X86_2_H +#define __BRANCH_X86_2_H + +#include "../../../Common/MyCom.h" +#include "../RangeCoder/RangeCoderBit.h" +#include "../../ICoder.h" + +namespace NCompress { +namespace NBcj2 { + +const int kNumMoveBits = 5; + +#ifndef EXTRACT_ONLY + +class CEncoder: + public ICompressCoder2, + public CMyUnknownImp +{ + Byte *_buffer; +public: + CEncoder(): _buffer(0) {}; + ~CEncoder(); + bool Create(); + + COutBuffer _mainStream; + COutBuffer _callStream; + COutBuffer _jumpStream; + NCompress::NRangeCoder::CEncoder _rangeEncoder; + NCompress::NRangeCoder::CBitEncoder _statusEncoder[256 + 2]; + + HRESULT Flush(); + void ReleaseStreams() + { + _mainStream.ReleaseStream(); + _callStream.ReleaseStream(); + _jumpStream.ReleaseStream(); + _rangeEncoder.ReleaseStream(); + } + + class CCoderReleaser + { + CEncoder *_coder; + public: + CCoderReleaser(CEncoder *coder): _coder(coder) {} + ~CCoderReleaser() { _coder->ReleaseStreams(); } + }; + +public: + + MY_UNKNOWN_IMP + + HRESULT CodeReal(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); +}; + +#endif + +class CDecoder: + public ICompressCoder2, + public CMyUnknownImp +{ +public: + CInBuffer _mainInStream; + CInBuffer _callStream; + CInBuffer _jumpStream; + NCompress::NRangeCoder::CDecoder _rangeDecoder; + NCompress::NRangeCoder::CBitDecoder _statusDecoder[256 + 2]; + + COutBuffer _outStream; + + void ReleaseStreams() + { + _mainInStream.ReleaseStream(); + _callStream.ReleaseStream(); + _jumpStream.ReleaseStream(); + _rangeDecoder.ReleaseStream(); + _outStream.ReleaseStream(); + } + + HRESULT Flush() { return _outStream.Flush(); } + class CCoderReleaser + { + CDecoder *_coder; + public: + CCoderReleaser(CDecoder *coder): _coder(coder) {} + ~CCoderReleaser() { _coder->ReleaseStreams(); } + }; + +public: + MY_UNKNOWN_IMP + HRESULT CodeReal(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.cpp new file mode 100644 index 0000000..3f252f2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.cpp @@ -0,0 +1,38 @@ +// ByteSwap.cpp + +#include "StdAfx.h" + +#include "ByteSwap.h" + +STDMETHODIMP CByteSwap2::Init() { return S_OK; } + +STDMETHODIMP_(UInt32) CByteSwap2::Filter(Byte *data, UInt32 size) +{ + const UInt32 kStep = 2; + UInt32 i; + for (i = 0; i + kStep <= size; i += kStep) + { + Byte b = data[i]; + data[i] = data[i + 1]; + data[i + 1] = b; + } + return i; +} + +STDMETHODIMP CByteSwap4::Init() { return S_OK; } + +STDMETHODIMP_(UInt32) CByteSwap4::Filter(Byte *data, UInt32 size) +{ + const UInt32 kStep = 4; + UInt32 i; + for (i = 0; i + kStep <= size; i += kStep) + { + Byte b0 = data[i]; + Byte b1 = data[i + 1]; + data[i] = data[i + 3]; + data[i + 1] = data[i + 2]; + data[i + 2] = b1; + data[i + 3] = b0; + } + return i; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.h new file mode 100644 index 0000000..0030306 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.h @@ -0,0 +1,37 @@ +// ByteSwap.h + +#ifndef __BYTESWAP_H +#define __BYTESWAP_H + +#include "../../ICoder.h" +#include "Common/MyCom.h" + +// {23170F69-40C1-278B-0203-020000000000} +DEFINE_GUID(CLSID_CCompressConvertByteSwap2, +0x23170F69, 0x40C1, 0x278B, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00); + +// {23170F69-40C1-278B-0203-040000000000} +DEFINE_GUID(CLSID_CCompressConvertByteSwap4, +0x23170F69, 0x40C1, 0x278B, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00); + +class CByteSwap2: + public ICompressFilter, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + STDMETHOD(Init)(); + STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); +}; + +class CByteSwap4: + public ICompressFilter, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + STDMETHOD(Init)(); + STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwapRegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwapRegister.cpp new file mode 100644 index 0000000..b1cf8fa --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwapRegister.cpp @@ -0,0 +1,17 @@ +// ByteSwapRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "ByteSwap.h" +static void *CreateCodec2() { return (void *)(ICompressFilter *)(new CByteSwap2); } +static void *CreateCodec4() { return (void *)(ICompressFilter *)(new CByteSwap4); } + +static CCodecInfo g_CodecsInfo[] = +{ + { CreateCodec2, CreateCodec4, 0x020302, L"Swap2", 1, true }, + { CreateCodec4, CreateCodec4, 0x020304, L"Swap4", 1, true } +}; + +REGISTER_CODECS(ByteSwap) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.h new file mode 100644 index 0000000..e7fb698 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/CodecExports.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/CodecExports.cpp new file mode 100644 index 0000000..a4a85b6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/CodecExports.cpp @@ -0,0 +1,157 @@ +// CodecExports.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" +#include "../../Windows/PropVariant.h" +#include "../Common/RegisterCodec.h" +#include "../ICoder.h" + +extern unsigned int g_NumCodecs; +extern const CCodecInfo *g_Codecs[]; + +static const UInt16 kDecodeId = 0x2790; + +DEFINE_GUID(CLSID_CCodec, +0x23170F69, 0x40C1, kDecodeId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +static inline HRESULT SetPropString(const char *s, unsigned int size, PROPVARIANT *value) +{ + if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0) + value->vt = VT_BSTR; + return S_OK; +} + +static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value) +{ + return SetPropString((const char *)&guid, sizeof(GUID), value); +} + +static HRESULT SetClassID(CMethodId id, bool encode, PROPVARIANT *value) +{ + GUID clsId = CLSID_CCodec; + for (int i = 0; i < sizeof(id); i++, id >>= 8) + clsId.Data4[i] = (Byte)(id & 0xFF); + if (encode) + clsId.Data3++; + return SetPropGUID(clsId, value); +} + +static HRESULT FindCodecClassId(const GUID *clsID, UInt32 isCoder2, bool isFilter, bool &encode, int &index) +{ + index = -1; + if (clsID->Data1 != CLSID_CCodec.Data1 || + clsID->Data2 != CLSID_CCodec.Data2 || + (clsID->Data3 & ~1) != kDecodeId) + return S_OK; + encode = (clsID->Data3 != kDecodeId); + UInt64 id = 0; + for (int j = 0; j < 8; j++) + id |= ((UInt64)clsID->Data4[j]) << (8 * j); + for (UInt32 i = 0; i < g_NumCodecs; i++) + { + const CCodecInfo &codec = *g_Codecs[i]; + if (id != codec.Id || encode && !codec.CreateEncoder || !encode && !codec.CreateDecoder) + continue; + if (!isFilter && codec.IsFilter || isFilter && !codec.IsFilter || + codec.NumInStreams != 1 && !isCoder2 || codec.NumInStreams == 1 && isCoder2) + return E_NOINTERFACE; + index = i; + return S_OK; + } + return S_OK; +} + +STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject) +{ + COM_TRY_BEGIN + *outObject = 0; + bool isCoder = (*iid == IID_ICompressCoder) != 0; + bool isCoder2 = (*iid == IID_ICompressCoder2) != 0; + bool isFilter = (*iid == IID_ICompressFilter) != 0; + const CCodecInfo &codec = *g_Codecs[index]; + if (!isFilter && codec.IsFilter || isFilter && !codec.IsFilter || + codec.NumInStreams != 1 && !isCoder2 || codec.NumInStreams == 1 && isCoder2) + return E_NOINTERFACE; + if (encode) + { + if (!codec.CreateEncoder) + return CLASS_E_CLASSNOTAVAILABLE; + *outObject = codec.CreateEncoder(); + } + else + { + if (!codec.CreateDecoder) + return CLASS_E_CLASSNOTAVAILABLE; + *outObject = codec.CreateDecoder(); + } + if (isCoder) + ((ICompressCoder *)*outObject)->AddRef(); + else if (isCoder2) + ((ICompressCoder2 *)*outObject)->AddRef(); + else + ((ICompressFilter *)*outObject)->AddRef(); + return S_OK; + COM_TRY_END +} + +STDAPI CreateCoder(const GUID *clsid, const GUID *iid, void **outObject) +{ + *outObject = 0; + bool isCoder = (*iid == IID_ICompressCoder) != 0; + bool isCoder2 = (*iid == IID_ICompressCoder2) != 0; + bool isFilter = (*iid == IID_ICompressFilter) != 0; + if (!isCoder && !isCoder2 && !isFilter) + return E_NOINTERFACE; + bool encode; + int codecIndex; + HRESULT res = FindCodecClassId(clsid, isCoder2, isFilter, encode, codecIndex); + if (res != S_OK) + return res; + if (codecIndex < 0) + return CLASS_E_CLASSNOTAVAILABLE; + return CreateCoder2(encode, codecIndex, iid, outObject); +} + +STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value) +{ + ::VariantClear((VARIANTARG *)value); + const CCodecInfo &codec = *g_Codecs[codecIndex]; + switch(propID) + { + case NMethodPropID::kID: + { + value->uhVal.QuadPart = (UInt64)codec.Id; + value->vt = VT_UI8; + break; + } + case NMethodPropID::kName: + if ((value->bstrVal = ::SysAllocString(codec.Name)) != 0) + value->vt = VT_BSTR; + break; + case NMethodPropID::kDecoder: + if (codec.CreateDecoder) + return SetClassID(codec.Id, false, value); + break; + case NMethodPropID::kEncoder: + if (codec.CreateEncoder) + return SetClassID(codec.Id, true, value); + break; + case NMethodPropID::kInStreams: + { + if (codec.NumInStreams != 1) + { + value->vt = VT_UI4; + value->ulVal = codec.NumInStreams; + } + break; + } + } + return S_OK; +} + +STDAPI GetNumberOfMethods(UINT32 *numCodecs) +{ + *numCodecs = g_NumCodecs; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.cpp new file mode 100644 index 0000000..8e18db2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.cpp @@ -0,0 +1,62 @@ +// Compress/CopyCoder.cpp + +#include "StdAfx.h" + +extern "C" +{ +#include "../../../../C/Alloc.h" +} + +#include "CopyCoder.h" +#include "../../Common/StreamUtils.h" + +namespace NCompress { + +static const UInt32 kBufferSize = 1 << 17; + +CCopyCoder::~CCopyCoder() +{ + ::MidFree(_buffer); +} + +STDMETHODIMP CCopyCoder::Code(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 * /* inSize */, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + if (_buffer == 0) + { + _buffer = (Byte *)::MidAlloc(kBufferSize); + if (_buffer == 0) + return E_OUTOFMEMORY; + } + + TotalSize = 0; + for (;;) + { + UInt32 realProcessedSize; + UInt32 size = kBufferSize; + if (outSize != 0) + if (size > *outSize - TotalSize) + size = (UInt32)(*outSize - TotalSize); + RINOK(inStream->Read(_buffer, size, &realProcessedSize)); + if (realProcessedSize == 0) + break; + RINOK(WriteStream(outStream, _buffer, realProcessedSize, NULL)); + TotalSize += realProcessedSize; + if (progress != NULL) + { + RINOK(progress->SetRatioInfo(&TotalSize, &TotalSize)); + } + } + return S_OK; +} + +STDMETHODIMP CCopyCoder::GetInStreamProcessedSize(UInt64 *value) +{ + *value = TotalSize; + return S_OK; +} + +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.h new file mode 100644 index 0000000..d453069 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.h @@ -0,0 +1,33 @@ +// Compress/CopyCoder.h + +#ifndef __COMPRESS_COPYCODER_H +#define __COMPRESS_COPYCODER_H + +#include "../../ICoder.h" +#include "../../../Common/MyCom.h" + +namespace NCompress { + +class CCopyCoder: + public ICompressCoder, + public ICompressGetInStreamProcessedSize, + public CMyUnknownImp +{ + Byte *_buffer; +public: + UInt64 TotalSize; + CCopyCoder(): TotalSize(0) , _buffer(0) {}; + ~CCopyCoder(); + + MY_UNKNOWN_IMP1(ICompressGetInStreamProcessedSize) + + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyRegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyRegister.cpp new file mode 100644 index 0000000..1e688b7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/CopyRegister.cpp @@ -0,0 +1,13 @@ +// LZMARegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "CopyCoder.h" +static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::CCopyCoder); } + +static CCodecInfo g_CodecInfo = +{ CreateCodec, CreateCodec, 0x00, L"Copy", 1, false }; + +REGISTER_CODEC(Copy) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.h new file mode 100644 index 0000000..92239ae --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/Copy/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.cpp new file mode 100644 index 0000000..9de2d06 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.cpp @@ -0,0 +1,16 @@ +// LZOutWindow.cpp + +#include "StdAfx.h" + +#include "LZOutWindow.h" + +void CLZOutWindow::Init(bool solid) +{ + if(!solid) + COutBuffer::Init(); + #ifdef _NO_EXCEPTIONS + ErrorCode = S_OK; + #endif +} + + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.h new file mode 100644 index 0000000..60b1b8a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.h @@ -0,0 +1,65 @@ +// LZOutWindow.h + +#ifndef __LZ_OUT_WINDOW_H +#define __LZ_OUT_WINDOW_H + +#include "../../IStream.h" +#include "../../Common/OutBuffer.h" + +#ifndef _NO_EXCEPTIONS +typedef COutBufferException CLZOutWindowException; +#endif + +class CLZOutWindow: public COutBuffer +{ +public: + void Init(bool solid = false); + + // distance >= 0, len > 0, + bool CopyBlock(UInt32 distance, UInt32 len) + { + UInt32 pos = _pos - distance - 1; + if (distance >= _pos) + { + if (!_overDict || distance >= _bufferSize) + return false; + pos += _bufferSize; + } + if (_limitPos - _pos > len && _bufferSize - pos > len) + { + const Byte *src = _buffer + pos; + Byte *dest = _buffer + _pos; + _pos += len; + do + *dest++ = *src++; + while(--len != 0); + } + else do + { + if (pos == _bufferSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos == _limitPos) + FlushWithCheck(); + } + while(--len != 0); + return true; + } + + void PutByte(Byte b) + { + _buffer[_pos++] = b; + if (_pos == _limitPos) + FlushWithCheck(); + } + + Byte GetByte(UInt32 distance) const + { + UInt32 pos = _pos - distance - 1; + if (pos >= _bufferSize) + pos += _bufferSize; + return _buffer[pos]; + } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/StdAfx.h new file mode 100644 index 0000000..3ff6d8a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZ/StdAfx.h @@ -0,0 +1,6 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMA.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMA.h new file mode 100644 index 0000000..7bc4c43 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMA.h @@ -0,0 +1,82 @@ +// LZMA.h + +#ifndef __LZMA_H +#define __LZMA_H + +namespace NCompress { +namespace NLZMA { + +const UInt32 kNumRepDistances = 4; + +const int kNumStates = 12; + +const Byte kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +const Byte kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +const Byte kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +const Byte kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +class CState +{ +public: + Byte Index; + void Init() { Index = 0; } + void UpdateChar() { Index = kLiteralNextStates[Index]; } + void UpdateMatch() { Index = kMatchNextStates[Index]; } + void UpdateRep() { Index = kRepNextStates[Index]; } + void UpdateShortRep() { Index = kShortRepNextStates[Index]; } + bool IsCharState() const { return Index < 7; } +}; + +const int kNumPosSlotBits = 6; +const int kDicLogSizeMin = 0; +const int kDicLogSizeMax = 32; +const int kDistTableSizeMax = kDicLogSizeMax * 2; + +const UInt32 kNumLenToPosStates = 4; + +inline UInt32 GetLenToPosState(UInt32 len) +{ + len -= 2; + if (len < kNumLenToPosStates) + return len; + return kNumLenToPosStates - 1; +} + +namespace NLength { + +const int kNumPosStatesBitsMax = 4; +const UInt32 kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + +const int kNumPosStatesBitsEncodingMax = 4; +const UInt32 kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + +const int kNumLowBits = 3; +const int kNumMidBits = 3; +const int kNumHighBits = 8; +const UInt32 kNumLowSymbols = 1 << kNumLowBits; +const UInt32 kNumMidSymbols = 1 << kNumMidBits; +const UInt32 kNumSymbolsTotal = kNumLowSymbols + kNumMidSymbols + (1 << kNumHighBits); + +} + +const UInt32 kMatchMinLen = 2; +const UInt32 kMatchMaxLen = kMatchMinLen + NLength::kNumSymbolsTotal - 1; + +const int kNumAlignBits = 4; +const UInt32 kAlignTableSize = 1 << kNumAlignBits; +const UInt32 kAlignMask = (kAlignTableSize - 1); + +const UInt32 kStartPosModelIndex = 4; +const UInt32 kEndPosModelIndex = 14; +const UInt32 kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + +const UInt32 kNumFullDistances = 1 << (kEndPosModelIndex / 2); + +const int kNumLitPosStatesBitsEncodingMax = 4; +const int kNumLitContextBitsMax = 8; + +const int kNumMoveBits = 5; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.cpp new file mode 100644 index 0000000..1b73855 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.cpp @@ -0,0 +1,338 @@ +// LZMADecoder.cpp + +#include "StdAfx.h" + +#include "LZMADecoder.h" +#include "../../../Common/Defs.h" + +namespace NCompress { +namespace NLZMA { + +const int kLenIdFinished = -1; +const int kLenIdNeedInit = -2; + +void CDecoder::Init() +{ + { + for(int i = 0; i < kNumStates; i++) + { + for (UInt32 j = 0; j <= _posStateMask; j++) + { + _isMatch[i][j].Init(); + _isRep0Long[i][j].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + } + { + for (UInt32 i = 0; i < kNumLenToPosStates; i++) + _posSlotDecoder[i].Init(); + } + { + for(UInt32 i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + _posDecoders[i].Init(); + } + _posAlignDecoder.Init(); + _lenDecoder.Init(_posStateMask + 1); + _repMatchLenDecoder.Init(_posStateMask + 1); + _literalDecoder.Init(); + + _state.Init(); + _reps[0] = _reps[1] = _reps[2] = _reps[3] = 0; +} + +HRESULT CDecoder::CodeSpec(UInt32 curSize) +{ + if (_outSizeDefined) + { + const UInt64 rem = _outSize - _outWindowStream.GetProcessedSize(); + if (curSize > rem) + curSize = (UInt32)rem; + } + + if (_remainLen == kLenIdFinished) + return S_OK; + if (_remainLen == kLenIdNeedInit) + { + _rangeDecoder.Init(); + Init(); + _remainLen = 0; + } + if (curSize == 0) + return S_OK; + + UInt32 rep0 = _reps[0]; + UInt32 rep1 = _reps[1]; + UInt32 rep2 = _reps[2]; + UInt32 rep3 = _reps[3]; + CState state = _state; + Byte previousByte; + + while(_remainLen > 0 && curSize > 0) + { + previousByte = _outWindowStream.GetByte(rep0); + _outWindowStream.PutByte(previousByte); + _remainLen--; + curSize--; + } + UInt64 nowPos64 = _outWindowStream.GetProcessedSize(); + if (nowPos64 == 0) + previousByte = 0; + else + previousByte = _outWindowStream.GetByte(0); + + while(curSize > 0) + { + { + #ifdef _NO_EXCEPTIONS + if (_rangeDecoder.Stream.ErrorCode != S_OK) + return _rangeDecoder.Stream.ErrorCode; + #endif + if (_rangeDecoder.Stream.WasFinished()) + return S_FALSE; + UInt32 posState = UInt32(nowPos64) & _posStateMask; + if (_isMatch[state.Index][posState].Decode(&_rangeDecoder) == 0) + { + if(!state.IsCharState()) + previousByte = _literalDecoder.DecodeWithMatchByte(&_rangeDecoder, + (UInt32)nowPos64, previousByte, _outWindowStream.GetByte(rep0)); + else + previousByte = _literalDecoder.DecodeNormal(&_rangeDecoder, + (UInt32)nowPos64, previousByte); + _outWindowStream.PutByte(previousByte); + state.UpdateChar(); + curSize--; + nowPos64++; + } + else + { + UInt32 len; + if(_isRep[state.Index].Decode(&_rangeDecoder) == 1) + { + len = 0; + if(_isRepG0[state.Index].Decode(&_rangeDecoder) == 0) + { + if(_isRep0Long[state.Index][posState].Decode(&_rangeDecoder) == 0) + { + state.UpdateShortRep(); + len = 1; + } + } + else + { + UInt32 distance; + if(_isRepG1[state.Index].Decode(&_rangeDecoder) == 0) + distance = rep1; + else + { + if (_isRepG2[state.Index].Decode(&_rangeDecoder) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + if (len == 0) + { + len = _repMatchLenDecoder.Decode(&_rangeDecoder, posState) + kMatchMinLen; + state.UpdateRep(); + } + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = kMatchMinLen + _lenDecoder.Decode(&_rangeDecoder, posState); + state.UpdateMatch(); + UInt32 posSlot = _posSlotDecoder[GetLenToPosState(len)].Decode(&_rangeDecoder); + if (posSlot >= kStartPosModelIndex) + { + UInt32 numDirectBits = (posSlot >> 1) - 1; + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + + if (posSlot < kEndPosModelIndex) + rep0 += NRangeCoder::ReverseBitTreeDecode(_posDecoders + + rep0 - posSlot - 1, &_rangeDecoder, numDirectBits); + else + { + rep0 += (_rangeDecoder.DecodeDirectBits( + numDirectBits - kNumAlignBits) << kNumAlignBits); + rep0 += _posAlignDecoder.ReverseDecode(&_rangeDecoder); + if (rep0 == 0xFFFFFFFF) + { + _remainLen = kLenIdFinished; + return S_OK; + } + } + } + else + rep0 = posSlot; + } + UInt32 locLen = len; + if (len > curSize) + locLen = (UInt32)curSize; + if (!_outWindowStream.CopyBlock(rep0, locLen)) + return S_FALSE; + previousByte = _outWindowStream.GetByte(0); + curSize -= locLen; + nowPos64 += locLen; + len -= locLen; + if (len != 0) + { + _remainLen = (Int32)len; + break; + } + + #ifdef _NO_EXCEPTIONS + if (_outWindowStream.ErrorCode != S_OK) + return _outWindowStream.ErrorCode; + #endif + } + } + } + if (_rangeDecoder.Stream.WasFinished()) + return S_FALSE; + _reps[0] = rep0; + _reps[1] = rep1; + _reps[2] = rep2; + _reps[3] = rep3; + _state = state; + + return S_OK; +} + +STDMETHODIMP CDecoder::CodeReal(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + SetInStream(inStream); + _outWindowStream.SetStream(outStream); + SetOutStreamSize(outSize); + CDecoderFlusher flusher(this); + + for (;;) + { + UInt32 curSize = 1 << 18; + RINOK(CodeSpec(curSize)); + if (_remainLen == kLenIdFinished) + break; + if (progress != NULL) + { + UInt64 inSize = _rangeDecoder.GetProcessedSize(); + UInt64 nowPos64 = _outWindowStream.GetProcessedSize(); + RINOK(progress->SetRatioInfo(&inSize, &nowPos64)); + } + if (_outSizeDefined) + if (_outWindowStream.GetProcessedSize() >= _outSize) + break; + } + flusher.NeedFlush = false; + return Flush(); +} + + +#ifdef _NO_EXCEPTIONS + +#define LZMA_TRY_BEGIN +#define LZMA_TRY_END + +#else + +#define LZMA_TRY_BEGIN try { +#define LZMA_TRY_END } \ + catch(const CInBufferException &e) { return e.ErrorCode; } \ + catch(const CLZOutWindowException &e) { return e.ErrorCode; } \ + catch(...) { return S_FALSE; } + +#endif + + +STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + LZMA_TRY_BEGIN + return CodeReal(inStream, outStream, inSize, outSize, progress); + LZMA_TRY_END +} + +STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *properties, UInt32 size) +{ + if (size < 5) + return E_INVALIDARG; + int lc = properties[0] % 9; + Byte remainder = (Byte)(properties[0] / 9); + int lp = remainder % 5; + int pb = remainder / 5; + if (pb > NLength::kNumPosStatesBitsMax) + return E_INVALIDARG; + _posStateMask = (1 << pb) - 1; + UInt32 dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); + if (!_outWindowStream.Create(dictionarySize)) + return E_OUTOFMEMORY; + if (!_literalDecoder.Create(lp, lc)) + return E_OUTOFMEMORY; + if (!_rangeDecoder.Create(1 << 20)) + return E_OUTOFMEMORY; + return S_OK; +} + +STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) +{ + *value = _rangeDecoder.GetProcessedSize(); + return S_OK; +} + +STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) +{ + _rangeDecoder.SetStream(inStream); + return S_OK; +} + +STDMETHODIMP CDecoder::ReleaseInStream() +{ + _rangeDecoder.ReleaseStream(); + return S_OK; +} + +STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) +{ + _outSizeDefined = (outSize != NULL); + if (_outSizeDefined) + _outSize = *outSize; + _remainLen = kLenIdNeedInit; + _outWindowStream.Init(); + return S_OK; +} + +#ifndef NO_READ_FROM_CODER + +STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + LZMA_TRY_BEGIN + if (processedSize) + *processedSize = 0; + const UInt64 startPos = _outWindowStream.GetProcessedSize(); + _outWindowStream.SetMemStream((Byte *)data); + RINOK(CodeSpec(size)); + if (processedSize) + *processedSize = (UInt32)(_outWindowStream.GetProcessedSize() - startPos); + return Flush(); + LZMA_TRY_END +} + +#endif + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.h new file mode 100644 index 0000000..bc44a5a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.h @@ -0,0 +1,255 @@ +// LZMA/Decoder.h + +#ifndef __LZMA_DECODER_H +#define __LZMA_DECODER_H + +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" +#include "../LZ/LZOutWindow.h" +#include "../RangeCoder/RangeCoderBitTree.h" + +extern "C" +{ + #include "../../../../C/Alloc.h" +} + +#include "LZMA.h" + +namespace NCompress { +namespace NLZMA { + +typedef NRangeCoder::CBitDecoder CMyBitDecoder; + +class CLiteralDecoder2 +{ + CMyBitDecoder _decoders[0x300]; +public: + void Init() + { + for (int i = 0; i < 0x300; i++) + _decoders[i].Init(); + } + Byte DecodeNormal(NRangeCoder::CDecoder *rangeDecoder) + { + UInt32 symbol = 1; + RC_INIT_VAR + do + { + // symbol = (symbol << 1) | _decoders[0][symbol].Decode(rangeDecoder); + RC_GETBIT(kNumMoveBits, _decoders[symbol].Prob, symbol) + } + while (symbol < 0x100); + RC_FLUSH_VAR + return (Byte)symbol; + } + Byte DecodeWithMatchByte(NRangeCoder::CDecoder *rangeDecoder, Byte matchByte) + { + UInt32 symbol = 1; + RC_INIT_VAR + do + { + UInt32 matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + // UInt32 bit = _decoders[1 + matchBit][symbol].Decode(rangeDecoder); + // symbol = (symbol << 1) | bit; + UInt32 bit; + RC_GETBIT2(kNumMoveBits, _decoders[0x100 + (matchBit << 8) + symbol].Prob, symbol, + bit = 0, bit = 1) + if (matchBit != bit) + { + while (symbol < 0x100) + { + // symbol = (symbol << 1) | _decoders[0][symbol].Decode(rangeDecoder); + RC_GETBIT(kNumMoveBits, _decoders[symbol].Prob, symbol) + } + break; + } + } + while (symbol < 0x100); + RC_FLUSH_VAR + return (Byte)symbol; + } +}; + +class CLiteralDecoder +{ + CLiteralDecoder2 *_coders; + int _numPrevBits; + int _numPosBits; + UInt32 _posMask; +public: + CLiteralDecoder(): _coders(0) {} + ~CLiteralDecoder() { Free(); } + void Free() + { + MyFree(_coders); + _coders = 0; + } + bool Create(int numPosBits, int numPrevBits) + { + if (_coders == 0 || (numPosBits + numPrevBits) != + (_numPrevBits + _numPosBits) ) + { + Free(); + UInt32 numStates = 1 << (numPosBits + numPrevBits); + _coders = (CLiteralDecoder2 *)MyAlloc(numStates * sizeof(CLiteralDecoder2)); + } + _numPosBits = numPosBits; + _posMask = (1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + return (_coders != 0); + } + void Init() + { + UInt32 numStates = 1 << (_numPrevBits + _numPosBits); + for (UInt32 i = 0; i < numStates; i++) + _coders[i].Init(); + } + UInt32 GetState(UInt32 pos, Byte prevByte) const + { return ((pos & _posMask) << _numPrevBits) + (prevByte >> (8 - _numPrevBits)); } + Byte DecodeNormal(NRangeCoder::CDecoder *rangeDecoder, UInt32 pos, Byte prevByte) + { return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } + Byte DecodeWithMatchByte(NRangeCoder::CDecoder *rangeDecoder, UInt32 pos, Byte prevByte, Byte matchByte) + { return _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } +}; + +namespace NLength { + +class CDecoder +{ + CMyBitDecoder _choice; + CMyBitDecoder _choice2; + NRangeCoder::CBitTreeDecoder _lowCoder[kNumPosStatesMax]; + NRangeCoder::CBitTreeDecoder _midCoder[kNumPosStatesMax]; + NRangeCoder::CBitTreeDecoder _highCoder; +public: + void Init(UInt32 numPosStates) + { + _choice.Init(); + _choice2.Init(); + for (UInt32 posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + UInt32 Decode(NRangeCoder::CDecoder *rangeDecoder, UInt32 posState) + { + if(_choice.Decode(rangeDecoder) == 0) + return _lowCoder[posState].Decode(rangeDecoder); + if(_choice2.Decode(rangeDecoder) == 0) + return kNumLowSymbols + _midCoder[posState].Decode(rangeDecoder); + return kNumLowSymbols + kNumMidSymbols + _highCoder.Decode(rangeDecoder); + } +}; + +} + +class CDecoder: + public ICompressCoder, + public ICompressSetDecoderProperties2, + public ICompressGetInStreamProcessedSize, + #ifndef NO_READ_FROM_CODER + public ICompressSetInStream, + public ICompressSetOutStreamSize, + public ISequentialInStream, + #endif + public CMyUnknownImp +{ + CLZOutWindow _outWindowStream; + NRangeCoder::CDecoder _rangeDecoder; + + CMyBitDecoder _isMatch[kNumStates][NLength::kNumPosStatesMax]; + CMyBitDecoder _isRep[kNumStates]; + CMyBitDecoder _isRepG0[kNumStates]; + CMyBitDecoder _isRepG1[kNumStates]; + CMyBitDecoder _isRepG2[kNumStates]; + CMyBitDecoder _isRep0Long[kNumStates][NLength::kNumPosStatesMax]; + + NRangeCoder::CBitTreeDecoder _posSlotDecoder[kNumLenToPosStates]; + + CMyBitDecoder _posDecoders[kNumFullDistances - kEndPosModelIndex]; + NRangeCoder::CBitTreeDecoder _posAlignDecoder; + + NLength::CDecoder _lenDecoder; + NLength::CDecoder _repMatchLenDecoder; + + CLiteralDecoder _literalDecoder; + + UInt32 _posStateMask; + + /////////////////// + // State + UInt32 _reps[4]; + CState _state; + Int32 _remainLen; // -1 means end of stream. // -2 means need Init + UInt64 _outSize; + bool _outSizeDefined; + + void Init(); + HRESULT CodeSpec(UInt32 size); +public: + + #ifndef NO_READ_FROM_CODER + MY_UNKNOWN_IMP5( + ICompressSetDecoderProperties2, + ICompressGetInStreamProcessedSize, + ICompressSetInStream, + ICompressSetOutStreamSize, + ISequentialInStream) + #else + MY_UNKNOWN_IMP2( + ICompressSetDecoderProperties2, + ICompressGetInStreamProcessedSize) + #endif + + void ReleaseStreams() + { + _outWindowStream.ReleaseStream(); + ReleaseInStream(); + } + + class CDecoderFlusher + { + CDecoder *_decoder; + public: + bool NeedFlush; + CDecoderFlusher(CDecoder *decoder): _decoder(decoder), NeedFlush(true) {} + ~CDecoderFlusher() + { + if (NeedFlush) + _decoder->Flush(); + _decoder->ReleaseStreams(); + } + }; + + HRESULT Flush() { return _outWindowStream.Flush(); } + + STDMETHOD(CodeReal)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + + STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); + + STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); + + STDMETHOD(SetInStream)(ISequentialInStream *inStream); + STDMETHOD(ReleaseInStream)(); + STDMETHOD(SetOutStreamSize)(const UInt64 *outSize); + + #ifndef NO_READ_FROM_CODER + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + #endif + + CDecoder(): _outSizeDefined(false) {} + virtual ~CDecoder() {} +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.cpp new file mode 100644 index 0000000..021099d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.cpp @@ -0,0 +1,1547 @@ +// LZMA/Encoder.cpp + +#include "StdAfx.h" + +#include + +#ifdef _WIN32 +#define USE_ALLOCA +#endif + +#ifdef USE_ALLOCA +#ifdef _WIN32 +#include +#else +#include +#endif +#endif + +#include "../../../Common/Defs.h" +#include "../../Common/StreamUtils.h" + +#include "LZMAEncoder.h" + +// extern "C" { #include "../../../../C/7zCrc.h" } + +// #define SHOW_STAT + + +namespace NCompress { +namespace NLZMA { + +// struct CCrcInit { CCrcInit() { InitCrcTable(); } } g_CrcInit; + +const int kDefaultDictionaryLogSize = 22; +const UInt32 kNumFastBytesDefault = 0x20; + +#ifndef LZMA_LOG_BSR +Byte g_FastPos[1 << kNumLogBits]; + +class CFastPosInit +{ +public: + CFastPosInit() { Init(); } + void Init() + { + const Byte kFastSlots = kNumLogBits * 2; + int c = 2; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) + { + UInt32 k = (1 << ((slotFast >> 1) - 1)); + for (UInt32 j = 0; j < k; j++, c++) + g_FastPos[c] = slotFast; + } + } +} g_FastPosInit; +#endif + +void CLiteralEncoder2::Encode(NRangeCoder::CEncoder *rangeEncoder, Byte symbol) +{ + UInt32 context = 1; + int i = 8; + do + { + i--; + UInt32 bit = (symbol >> i) & 1; + _encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + while(i != 0); +} + +void CLiteralEncoder2::EncodeMatched(NRangeCoder::CEncoder *rangeEncoder, + Byte matchByte, Byte symbol) +{ + UInt32 context = 1; + int i = 8; + do + { + i--; + UInt32 bit = (symbol >> i) & 1; + UInt32 matchBit = (matchByte >> i) & 1; + _encoders[0x100 + (matchBit << 8) + context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + if (matchBit != bit) + { + while(i != 0) + { + i--; + UInt32 bit = (symbol >> i) & 1; + _encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + break; + } + } + while(i != 0); +} + +UInt32 CLiteralEncoder2::GetPrice(bool matchMode, Byte matchByte, Byte symbol) const +{ + UInt32 price = 0; + UInt32 context = 1; + int i = 8; + if (matchMode) + { + do + { + i--; + UInt32 matchBit = (matchByte >> i) & 1; + UInt32 bit = (symbol >> i) & 1; + price += _encoders[0x100 + (matchBit << 8) + context].GetPrice(bit); + context = (context << 1) | bit; + if (matchBit != bit) + break; + } + while (i != 0); + } + while(i != 0) + { + i--; + UInt32 bit = (symbol >> i) & 1; + price += _encoders[context].GetPrice(bit); + context = (context << 1) | bit; + } + return price; +}; + + +namespace NLength { + +void CEncoder::Init(UInt32 numPosStates) +{ + _choice.Init(); + _choice2.Init(); + for (UInt32 posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); +} + +void CEncoder::Encode(NRangeCoder::CEncoder *rangeEncoder, UInt32 symbol, UInt32 posState) +{ + if(symbol < kNumLowSymbols) + { + _choice.Encode(rangeEncoder, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + _choice.Encode(rangeEncoder, 1); + if(symbol < kNumLowSymbols + kNumMidSymbols) + { + _choice2.Encode(rangeEncoder, 0); + _midCoder[posState].Encode(rangeEncoder, symbol - kNumLowSymbols); + } + else + { + _choice2.Encode(rangeEncoder, 1); + _highCoder.Encode(rangeEncoder, symbol - kNumLowSymbols - kNumMidSymbols); + } + } +} + +void CEncoder::SetPrices(UInt32 posState, UInt32 numSymbols, UInt32 *prices) const +{ + UInt32 a0 = _choice.GetPrice0(); + UInt32 a1 = _choice.GetPrice1(); + UInt32 b0 = a1 + _choice2.GetPrice0(); + UInt32 b1 = a1 + _choice2.GetPrice1(); + UInt32 i = 0; + for (i = 0; i < kNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < kNumLowSymbols + kNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + _midCoder[posState].GetPrice(i - kNumLowSymbols); + } + for (; i < numSymbols; i++) + prices[i] = b1 + _highCoder.GetPrice(i - kNumLowSymbols - kNumMidSymbols); +} + +} + +CEncoder::CEncoder(): + _numFastBytes(kNumFastBytesDefault), + _distTableSize(kDefaultDictionaryLogSize * 2), + _posStateBits(2), + _posStateMask(4 - 1), + _numLiteralPosStateBits(0), + _numLiteralContextBits(3), + _dictionarySize(1 << kDefaultDictionaryLogSize), + _matchFinderCycles(0), + #ifdef COMPRESS_MF_MT + _multiThread(false), + #endif + _writeEndMark(false) +{ + MatchFinder_Construct(&_matchFinderBase); + // _maxMode = false; + _fastMode = false; + #ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&_matchFinderMt); + _matchFinderMt.MatchFinder = &_matchFinderBase; + #endif +} + + +static void *SzAlloc(size_t size) { return BigAlloc(size); } +static void SzFree(void *address) { BigFree(address); } +ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +CEncoder::~CEncoder() +{ + #ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&_matchFinderMt, &g_Alloc); + #endif + MatchFinder_Free(&_matchFinderBase, &g_Alloc); +} + +static const UInt32 kBigHashDicLimit = (UInt32)1 << 24; + +HRESULT CEncoder::Create() +{ + if (!_rangeEncoder.Create(1 << 20)) + return E_OUTOFMEMORY; + bool btMode = (_matchFinderBase.btMode != 0); + #ifdef COMPRESS_MF_MT + _mtMode = (_multiThread && !_fastMode && btMode); + #endif + + if (!_literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits)) + return E_OUTOFMEMORY; + + _matchFinderBase.bigHash = (_dictionarySize > kBigHashDicLimit); + + UInt32 numCycles = 16 + (_numFastBytes >> 1); + if (!btMode) + numCycles >>= 1; + if (_matchFinderCycles != 0) + numCycles = _matchFinderCycles; + _matchFinderBase.cutValue = numCycles; + #ifdef COMPRESS_MF_MT + if (_mtMode) + { + RINOK(MatchFinderMt_Create(&_matchFinderMt, _dictionarySize, kNumOpts, _numFastBytes, kMatchMaxLen, &g_Alloc)); + _matchFinderObj = &_matchFinderMt; + MatchFinderMt_CreateVTable(&_matchFinderMt, &_matchFinder); + } + else + #endif + { + if (!MatchFinder_Create(&_matchFinderBase, _dictionarySize, kNumOpts, _numFastBytes, kMatchMaxLen, &g_Alloc)) + return E_OUTOFMEMORY; + _matchFinderObj = &_matchFinderBase; + MatchFinder_CreateVTable(&_matchFinderBase, &_matchFinder); + } + return S_OK; +} + +inline wchar_t GetUpperChar(wchar_t c) +{ + if (c >= 'a' && c <= 'z') + c -= 0x20; + return c; +} + +static int ParseMatchFinder(const wchar_t *s, int *btMode, UInt32 *numHashBytes /* , int *skipModeBits */) +{ + wchar_t c = GetUpperChar(*s++); + if (c == L'H') + { + if (GetUpperChar(*s++) != L'C') + return 0; + int numHashBytesLoc = (int)(*s++ - L'0'); + if (numHashBytesLoc < 4 || numHashBytesLoc > 4) + return 0; + if (*s++ != 0) + return 0; + *btMode = 0; + *numHashBytes = numHashBytesLoc; + return 1; + } + if (c != L'B') + return 0; + + if (GetUpperChar(*s++) != L'T') + return 0; + int numHashBytesLoc = (int)(*s++ - L'0'); + if (numHashBytesLoc < 2 || numHashBytesLoc > 4) + return 0; + c = GetUpperChar(*s++); + /* + int skipModeBitsLoc = 0; + if (c == L'D') + { + skipModeBitsLoc = 2; + c = GetUpperChar(*s++); + } + */ + if (c != L'\0') + return 0; + *btMode = 1; + *numHashBytes = numHashBytesLoc; + // *skipModeBits = skipModeBitsLoc; + return 1; +} + +STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, + const PROPVARIANT *properties, UInt32 numProperties) +{ + for (UInt32 i = 0; i < numProperties; i++) + { + const PROPVARIANT &prop = properties[i]; + switch(propIDs[i]) + { + case NCoderPropID::kNumFastBytes: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 numFastBytes = prop.ulVal; + if(numFastBytes < 5 || numFastBytes > kMatchMaxLen) + return E_INVALIDARG; + _numFastBytes = numFastBytes; + break; + } + case NCoderPropID::kMatchFinderCycles: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + _matchFinderCycles = prop.ulVal; + break; + } + case NCoderPropID::kAlgorithm: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 maximize = prop.ulVal; + _fastMode = (maximize == 0); + // _maxMode = (maximize >= 2); + break; + } + case NCoderPropID::kMatchFinder: + { + if (prop.vt != VT_BSTR) + return E_INVALIDARG; + if (!ParseMatchFinder(prop.bstrVal, &_matchFinderBase.btMode, &_matchFinderBase.numHashBytes /* , &_matchFinderBase.skipModeBits */)) + return E_INVALIDARG; + break; + } + case NCoderPropID::kMultiThread: + { + if (prop.vt != VT_BOOL) + return E_INVALIDARG; + #ifdef COMPRESS_MF_MT + Bool newMultiThread = (prop.boolVal == VARIANT_TRUE); + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + #endif + break; + } + case NCoderPropID::kNumThreads: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + #ifdef COMPRESS_MF_MT + Bool newMultiThread = (prop.ulVal > 1) ? True : False; + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + #endif + break; + } + case NCoderPropID::kDictionarySize: + { + const int kDicLogSizeMaxCompress = 30; // must be <= ((kNumLogBits - 1) * 2) + 7 = 31; + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 dictionarySize = prop.ulVal; + if (dictionarySize < UInt32(1 << kDicLogSizeMin) || + dictionarySize > UInt32(1 << kDicLogSizeMaxCompress)) + return E_INVALIDARG; + _dictionarySize = dictionarySize; + UInt32 dicLogSize; + for(dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++) + if (dictionarySize <= (UInt32(1) << dicLogSize)) + break; + _distTableSize = dicLogSize * 2; + break; + } + case NCoderPropID::kPosStateBits: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 value = prop.ulVal; + if (value > (UInt32)NLength::kNumPosStatesBitsEncodingMax) + return E_INVALIDARG; + _posStateBits = value; + _posStateMask = (1 << _posStateBits) - 1; + break; + } + case NCoderPropID::kLitPosBits: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 value = prop.ulVal; + if (value > (UInt32)kNumLitPosStatesBitsEncodingMax) + return E_INVALIDARG; + _numLiteralPosStateBits = value; + break; + } + case NCoderPropID::kLitContextBits: + { + if (prop.vt != VT_UI4) + return E_INVALIDARG; + UInt32 value = prop.ulVal; + if (value > (UInt32)kNumLitContextBitsMax) + return E_INVALIDARG; + _numLiteralContextBits = value; + break; + } + case NCoderPropID::kEndMarker: + { + if (prop.vt != VT_BOOL) + return E_INVALIDARG; + SetWriteEndMarkerMode(prop.boolVal == VARIANT_TRUE); + break; + } + default: + return E_INVALIDARG; + } + } + return S_OK; +} + +STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) +{ + const UInt32 kPropSize = 5; + Byte properties[kPropSize]; + properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + for (int i = 0; i < 4; i++) + properties[1 + i] = Byte(_dictionarySize >> (8 * i)); + return WriteStream(outStream, properties, kPropSize, NULL); +} + +STDMETHODIMP CEncoder::SetOutStream(ISequentialOutStream *outStream) +{ + _rangeEncoder.SetStream(outStream); + return S_OK; +} + +STDMETHODIMP CEncoder::ReleaseOutStream() +{ + _rangeEncoder.ReleaseStream(); + return S_OK; +} + +HRESULT CEncoder::Init() +{ + CBaseState::Init(); + + _rangeEncoder.Init(); + + for(int i = 0; i < kNumStates; i++) + { + for (UInt32 j = 0; j <= _posStateMask; j++) + { + _isMatch[i][j].Init(); + _isRep0Long[i][j].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + + _literalEncoder.Init(); + + { + for(UInt32 i = 0; i < kNumLenToPosStates; i++) + _posSlotEncoder[i].Init(); + } + { + for(UInt32 i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + _posEncoders[i].Init(); + } + + _lenEncoder.Init(1 << _posStateBits); + _repMatchLenEncoder.Init(1 << _posStateBits); + + _posAlignEncoder.Init(); + + _longestMatchWasFound = false; + _optimumEndIndex = 0; + _optimumCurrentIndex = 0; + _additionalOffset = 0; + + return S_OK; +} + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +void CEncoder::MovePos(UInt32 num) +{ + #ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); + #endif + if (num != 0) + { + _additionalOffset += num; + _matchFinder.Skip(_matchFinderObj, num); + } +} + +UInt32 CEncoder::Backward(UInt32 &backRes, UInt32 cur) +{ + _optimumEndIndex = cur; + UInt32 posMem = _optimum[cur].PosPrev; + UInt32 backMem = _optimum[cur].BackPrev; + do + { + if (_optimum[cur].Prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem].PosPrev = posMem - 1; + if (_optimum[cur].Prev2) + { + _optimum[posMem - 1].Prev1IsChar = false; + _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; + _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + } + } + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = _optimum[posPrev].BackPrev; + posMem = _optimum[posPrev].PosPrev; + + _optimum[posPrev].BackPrev = backCur; + _optimum[posPrev].PosPrev = cur; + cur = posPrev; + } + while(cur != 0); + backRes = _optimum[0].BackPrev; + _optimumCurrentIndex = _optimum[0].PosPrev; + return _optimumCurrentIndex; +} + +/* +Out: + (lenRes == 1) && (backRes == 0xFFFFFFFF) means Literal +*/ + +UInt32 CEncoder::GetOptimum(UInt32 position, UInt32 &backRes) +{ + if(_optimumEndIndex != _optimumCurrentIndex) + { + const COptimal &optimum = _optimum[_optimumCurrentIndex]; + UInt32 lenRes = optimum.PosPrev - _optimumCurrentIndex; + backRes = optimum.BackPrev; + _optimumCurrentIndex = optimum.PosPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes(_matchFinderObj); + + UInt32 lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + lenMain = ReadMatchDistances(numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + const Byte *data = _matchFinder.GetPointerToCurrentPos(_matchFinderObj) - 1; + if (numAvailableBytes < 2) + { + backRes = (UInt32)(-1); + return 1; + } + if (numAvailableBytes > kMatchMaxLen) + numAvailableBytes = kMatchMaxLen; + + UInt32 reps[kNumRepDistances]; + UInt32 repLens[kNumRepDistances]; + UInt32 repMaxIndex = 0; + UInt32 i; + for(i = 0; i < kNumRepDistances; i++) + { + reps[i] = _repDistances[i]; + const Byte *data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + UInt32 lenTest; + for (lenTest = 2; lenTest < numAvailableBytes && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if(repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + UInt32 lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + UInt32 *matchDistances = _matchDistances; + if(lenMain >= _numFastBytes) + { + backRes = matchDistances[numDistancePairs - 1] + kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + Byte currentByte = *data; + Byte matchByte = *(data - (reps[0] + 1)); + + if(lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + { + backRes = (UInt32)-1; + return 1; + } + + _optimum[0].State = _state; + + UInt32 posState = (position & _posStateMask); + + _optimum[1].Price = _isMatch[_state.Index][posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _previousByte)->GetPrice(!_state.IsCharState(), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + UInt32 matchPrice = _isMatch[_state.Index][posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1(); + + if(matchByte == currentByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if(shortRepPrice < _optimum[1].Price) + { + _optimum[1].Price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + + if(lenEnd < 2) + { + backRes = _optimum[1].BackPrev; + return 1; + } + + _optimum[1].PosPrev = 0; + for (i = 0; i < kNumRepDistances; i++) + _optimum[0].Backs[i] = reps[i]; + + UInt32 len = lenEnd; + do + _optimum[len--].Price = kIfinityPrice; + while (len >= 2); + + for(i = 0; i < kNumRepDistances; i++) + { + UInt32 repLen = repLens[i]; + if (repLen < 2) + continue; + UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + COptimal &optimum = _optimum[repLen]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = i; + optimum.Prev1IsChar = false; + } + } + while(--repLen >= 2); + } + + UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0(); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= lenMain) + { + UInt32 offs = 0; + while (len > matchDistances[offs]) + offs += 2; + for(; ; len++) + { + UInt32 distance = matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); + COptimal &optimum = _optimum[len]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = distance + kNumRepDistances; + optimum.Prev1IsChar = false; + } + if (len == matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + + UInt32 cur = 0; + + for (;;) + { + cur++; + if(cur == lenEnd) + { + return Backward(backRes, cur); + } + UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes(_matchFinderObj); + UInt32 newLen, numDistancePairs; + newLen = ReadMatchDistances(numDistancePairs); + if(newLen >= _numFastBytes) + { + _numDistancePairs = numDistancePairs; + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(backRes, cur); + } + position++; + COptimal &curOptimum = _optimum[cur]; + UInt32 posPrev = curOptimum.PosPrev; + CState state; + if (curOptimum.Prev1IsChar) + { + posPrev--; + if (curOptimum.Prev2) + { + state = _optimum[curOptimum.PosPrev2].State; + if (curOptimum.BackPrev2 < kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + else + state = _optimum[posPrev].State; + state.UpdateChar(); + } + else + state = _optimum[posPrev].State; + if (posPrev == cur - 1) + { + if (curOptimum.IsShortRep()) + state.UpdateShortRep(); + else + state.UpdateChar(); + } + else + { + UInt32 pos; + if (curOptimum.Prev1IsChar && curOptimum.Prev2) + { + posPrev = curOptimum.PosPrev2; + pos = curOptimum.BackPrev2; + state.UpdateRep(); + } + else + { + pos = curOptimum.BackPrev; + if (pos < kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + const COptimal &prevOptimum = _optimum[posPrev]; + if (pos < kNumRepDistances) + { + reps[0] = prevOptimum.Backs[pos]; + UInt32 i; + for(i = 1; i <= pos; i++) + reps[i] = prevOptimum.Backs[i - 1]; + for(; i < kNumRepDistances; i++) + reps[i] = prevOptimum.Backs[i]; + } + else + { + reps[0] = (pos - kNumRepDistances); + for(UInt32 i = 1; i < kNumRepDistances; i++) + reps[i] = prevOptimum.Backs[i - 1]; + } + } + curOptimum.State = state; + for(UInt32 i = 0; i < kNumRepDistances; i++) + curOptimum.Backs[i] = reps[i]; + UInt32 curPrice = curOptimum.Price; + const Byte *data = _matchFinder.GetPointerToCurrentPos(_matchFinderObj) - 1; + const Byte currentByte = *data; + const Byte matchByte = *(data - (reps[0] + 1)); + + UInt32 posState = (position & _posStateMask); + + UInt32 curAnd1Price = curPrice + + _isMatch[state.Index][posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, *(data - 1))->GetPrice(!state.IsCharState(), matchByte, currentByte); + + COptimal &nextOptimum = _optimum[cur + 1]; + + bool nextIsChar = false; + if (curAnd1Price < nextOptimum.Price) + { + nextOptimum.Price = curAnd1Price; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + UInt32 matchPrice = curPrice + _isMatch[state.Index][posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1(); + + if(matchByte == currentByte && + !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if(shortRepPrice <= nextOptimum.Price) + { + nextOptimum.Price = shortRepPrice; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsShortRep(); + nextIsChar = true; + } + } + /* + if(newLen == 2 && matchDistances[2] >= kDistLimit2) // test it maybe set 2000 ? + continue; + */ + + numAvailableBytesFull = MyMin(kNumOpts - 1 - cur, numAvailableBytesFull); + UInt32 numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) + continue; + if (numAvailableBytes > _numFastBytes) + numAvailableBytes = _numFastBytes; + if (!nextIsChar && matchByte != currentByte) // speed optimization + { + // try Literal + rep0 + const Byte *data2 = data - (reps[0] + 1); + UInt32 limit = MyMin(numAvailableBytesFull, _numFastBytes + 1); + UInt32 temp; + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + UInt32 lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + CState state2 = state; + state2.UpdateChar(); + UInt32 posStateNext = (position + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAnd1Price + + _isMatch[state2.Index][posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + // for (; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = cur + 1 + lenTest2; + while(lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + COptimal &optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = false; + } + } + } + } + + UInt32 startLen = 2; // speed optimization + for(UInt32 repIndex = 0; repIndex < kNumRepDistances; repIndex++) + { + // UInt32 repLen = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], newLen); // test it; + const Byte *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + UInt32 lenTest; + for (lenTest = 2; lenTest < numAvailableBytes && data[lenTest] == data2[lenTest]; lenTest++); + while(lenEnd < cur + lenTest) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 lenTestTemp = lenTest; + UInt32 price = repMatchPrice + GetPureRepPrice(repIndex, state, posState); + do + { + UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(lenTest - 2, posState); + COptimal &optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = repIndex; + optimum.Prev1IsChar = false; + } + } + while(--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + // if (_maxMode) + { + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = MyMin(numAvailableBytesFull, lenTest2 + _numFastBytes); + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + CState state2 = state; + state2.UpdateRep(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = + price + _repMatchLenEncoder.GetPrice(lenTest - 2, posState) + + _isMatch[state2.Index][posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, data[lenTest - 1])->GetPrice( + true, data2[lenTest], data[lenTest]); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAndLenCharPrice + + _isMatch[state2.Index][posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = cur + lenTest + 1 + lenTest2; + while(lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + COptimal &optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = repIndex; + } + } + } + } + } + + // for(UInt32 lenTest = 2; lenTest <= newLen; lenTest++) + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for (numDistancePairs = 0; newLen > matchDistances[numDistancePairs]; numDistancePairs += 2); + matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + UInt32 normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0(); + while(lenEnd < cur + newLen) + _optimum[++lenEnd].Price = kIfinityPrice; + + UInt32 offs = 0; + while(startLen > matchDistances[offs]) + offs += 2; + UInt32 curBack = matchDistances[offs + 1]; + UInt32 posSlot = GetPosSlot2(curBack); + for(UInt32 lenTest = /*2*/ startLen; ; lenTest++) + { + UInt32 curAndLenPrice = normalMatchPrice; + UInt32 lenToPosState = GetLenToPosState(lenTest); + if (curBack < kNumFullDistances) + curAndLenPrice += _distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += _posSlotPrices[lenToPosState][posSlot] + _alignPrices[curBack & kAlignMask]; + + curAndLenPrice += _lenEncoder.GetPrice(lenTest - kMatchMinLen, posState); + + COptimal &optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = curBack + kNumRepDistances; + optimum.Prev1IsChar = false; + } + + if (/*_maxMode && */lenTest == matchDistances[offs]) + { + // Try Match + Literal + Rep0 + const Byte *data2 = data - (curBack + 1); + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = MyMin(numAvailableBytesFull, lenTest2 + _numFastBytes); + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + CState state2 = state; + state2.UpdateMatch(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + _isMatch[state2.Index][posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, data[lenTest - 1])->GetPrice( + true, data2[lenTest], data[lenTest]); + state2.UpdateChar(); + posStateNext = (posStateNext + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAndLenCharPrice + + _isMatch[state2.Index][posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = cur + lenTest + 1 + lenTest2; + while(lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + COptimal &optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = curBack + kNumRepDistances; + } + } + } + offs += 2; + if (offs == numDistancePairs) + break; + curBack = matchDistances[offs + 1]; + if (curBack >= kNumFullDistances) + posSlot = GetPosSlot2(curBack); + } + } + } + } +} + +static inline bool ChangePair(UInt32 smallDist, UInt32 bigDist) +{ + return ((bigDist >> 7) > smallDist); +} + +UInt32 CEncoder::ReadMatchDistances(UInt32 &numDistancePairs) +{ + UInt32 lenRes = 0; + numDistancePairs = _matchFinder.GetMatches(_matchFinderObj, _matchDistances); + #ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numDistancePairs / 2); + if (ttt >= 61994) + ttt = ttt; + + ttt++; + for (UInt32 i = 0; i < numDistancePairs; i += 2) + printf("%2d %6d | ", _matchDistances[i], _matchDistances[i + 1]); + #endif + if (numDistancePairs > 0) + { + lenRes = _matchDistances[numDistancePairs - 2]; + if (lenRes == _numFastBytes) + { + UInt32 numAvail = _matchFinder.GetNumAvailableBytes(_matchFinderObj) + 1; + const Byte *pby = _matchFinder.GetPointerToCurrentPos(_matchFinderObj) - 1; + UInt32 distance = _matchDistances[numDistancePairs - 1] + 1; + if (numAvail > kMatchMaxLen) + numAvail = kMatchMaxLen; + + const Byte *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + } + } + _additionalOffset++; + return lenRes; +} + +UInt32 CEncoder::GetOptimumFast(UInt32 &backRes) +{ + UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes(_matchFinderObj); + UInt32 lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + lenMain = ReadMatchDistances(numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + const Byte *data = _matchFinder.GetPointerToCurrentPos(_matchFinderObj) - 1; + if (numAvailableBytes > kMatchMaxLen) + numAvailableBytes = kMatchMaxLen; + if (numAvailableBytes < 2) + { + backRes = (UInt32)(-1); + return 1; + } + + UInt32 repLens[kNumRepDistances]; + UInt32 repMaxIndex = 0; + + for(UInt32 i = 0; i < kNumRepDistances; i++) + { + const Byte *data2 = data - (_repDistances[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + UInt32 len; + for (len = 2; len < numAvailableBytes && data[len] == data2[len]; len++); + if(len >= _numFastBytes) + { + backRes = i; + MovePos(len - 1); + return len; + } + repLens[i] = len; + if (len > repLens[repMaxIndex]) + repMaxIndex = i; + } + UInt32 *matchDistances = _matchDistances; + if(lenMain >= _numFastBytes) + { + backRes = matchDistances[numDistancePairs - 1] + kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + + UInt32 backMain = 0; // for GCC + if (lenMain >= 2) + { + backMain = matchDistances[numDistancePairs - 1]; + while (numDistancePairs > 2 && lenMain == matchDistances[numDistancePairs - 4] + 1) + { + if (!ChangePair(matchDistances[numDistancePairs - 3], backMain)) + break; + numDistancePairs -= 2; + lenMain = matchDistances[numDistancePairs - 2]; + backMain = matchDistances[numDistancePairs - 1]; + } + if (lenMain == 2 && backMain >= 0x80) + lenMain = 1; + } + + if (repLens[repMaxIndex] >= 2) + { + if (repLens[repMaxIndex] + 1 >= lenMain || + repLens[repMaxIndex] + 2 >= lenMain && (backMain > (1 << 9)) || + repLens[repMaxIndex] + 3 >= lenMain && (backMain > (1 << 15))) + { + backRes = repMaxIndex; + UInt32 lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + } + + if (lenMain >= 2 && numAvailableBytes > 2) + { + numAvailableBytes = _matchFinder.GetNumAvailableBytes(_matchFinderObj); + _longestMatchLength = ReadMatchDistances(_numDistancePairs); + if (_longestMatchLength >= 2) + { + UInt32 newDistance = matchDistances[_numDistancePairs - 1]; + if (_longestMatchLength >= lenMain && newDistance < backMain || + _longestMatchLength == lenMain + 1 && !ChangePair(backMain, newDistance) || + _longestMatchLength > lenMain + 1 || + _longestMatchLength + 1 >= lenMain && lenMain >= 3 && ChangePair(newDistance, backMain)) + { + _longestMatchWasFound = true; + backRes = UInt32(-1); + return 1; + } + } + data = _matchFinder.GetPointerToCurrentPos(_matchFinderObj) - 1; + for(UInt32 i = 0; i < kNumRepDistances; i++) + { + const Byte *data2 = data - (_repDistances[i] + 1); + if (data[1] != data2[1] || data[2] != data2[2]) + { + repLens[i] = 0; + continue; + } + UInt32 len; + for (len = 2; len < numAvailableBytes && data[len] == data2[len]; len++); + if (len + 1 >= lenMain) + { + _longestMatchWasFound = true; + backRes = UInt32(-1); + return 1; + } + } + backRes = backMain + kNumRepDistances; + MovePos(lenMain - 2); + return lenMain; + } + backRes = UInt32(-1); + return 1; +} + +HRESULT CEncoder::Flush(UInt32 nowPos) +{ + // ReleaseMFStream(); + if (_matchFinderBase.result != SZ_OK) + return _matchFinderBase.result; + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + return _rangeEncoder.FlushStream(); +} + +void CEncoder::WriteEndMarker(UInt32 posState) +{ + // This function for writing End Mark for stream version of LZMA. + // In current version this feature is not used. + if (!_writeEndMark) + return; + + _isMatch[_state.Index][posState].Encode(&_rangeEncoder, 1); + _isRep[_state.Index].Encode(&_rangeEncoder, 0); + _state.UpdateMatch(); + UInt32 len = kMatchMinLen; // kMatchMaxLen; + _lenEncoder.Encode(&_rangeEncoder, len - kMatchMinLen, posState, !_fastMode); + UInt32 posSlot = (1 << kNumPosSlotBits) - 1; + UInt32 lenToPosState = GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(&_rangeEncoder, posSlot); + UInt32 footerBits = 30; + UInt32 posReduced = (UInt32(1) << footerBits) - 1; + _rangeEncoder.EncodeDirectBits(posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + _posAlignEncoder.ReverseEncode(&_rangeEncoder, posReduced & kAlignMask); +} + +HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + // _needReleaseMFStream = false; + #ifdef COMPRESS_MF_MT + #ifdef USE_ALLOCA + alloca(0x300); + #endif + #endif + CCoderReleaser coderReleaser(this); + RINOK(SetStreams(inStream, outStream, inSize, outSize)); + for (;;) + { + UInt64 processedInSize; + UInt64 processedOutSize; + Int32 finished; + RINOK(CodeOneBlock(&processedInSize, &processedOutSize, &finished)); + if (finished != 0) + break; + if (progress != 0) + { + RINOK(progress->SetRatioInfo(&processedInSize, &processedOutSize)); + } + } + return S_OK; +} + +HRESULT CEncoder::SetStreams(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 * /* inSize */, const UInt64 * /* outSize */) +{ + _inStream = inStream; + _finished = false; + RINOK(Create()); + RINOK(SetOutStream(outStream)); + RINOK(Init()); + + if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - kMatchMinLen); + _lenEncoder.UpdateTables(1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - kMatchMinLen); + _repMatchLenEncoder.UpdateTables(1 << _posStateBits); + + nowPos64 = 0; + return S_OK; +} + +static HRes MyRead(void *object, void *data, UInt32 size, UInt32 *processedSize) +{ + return (HRes)((CSeqInStream *)object)->RealStream->Read(data, size, processedSize); +} + +HRESULT CEncoder::CodeOneBlock(UInt64 *inSize, UInt64 *outSize, Int32 *finished) +{ + if (_inStream != 0) + { + _seqInStream.RealStream = _inStream; + _seqInStream.SeqInStream.Read = MyRead; + _matchFinderBase.stream = &_seqInStream.SeqInStream; + _matchFinder.Init(_matchFinderObj); + _needReleaseMFStream = true; + _inStream = 0; + } + + + *finished = 1; + if (_finished) + return _matchFinderBase.result; + _finished = true; + + if (nowPos64 == 0) + { + if (_matchFinder.GetNumAvailableBytes(_matchFinderObj) == 0) + return Flush((UInt32)nowPos64); + UInt32 len, numDistancePairs; + len = ReadMatchDistances(numDistancePairs); + UInt32 posState = UInt32(nowPos64) & _posStateMask; + _isMatch[_state.Index][posState].Encode(&_rangeEncoder, 0); + _state.UpdateChar(); + Byte curByte = _matchFinder.GetIndexByte(_matchFinderObj, 0 - _additionalOffset); + _literalEncoder.GetSubCoder(UInt32(nowPos64), _previousByte)->Encode(&_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + nowPos64++; + } + + UInt32 nowPos32 = (UInt32)nowPos64; + UInt32 progressPosValuePrev = nowPos32; + + if (_matchFinder.GetNumAvailableBytes(_matchFinderObj) == 0) + return Flush(nowPos32); + + for (;;) + { + #ifdef _NO_EXCEPTIONS + if (_rangeEncoder.Stream.ErrorCode != S_OK) + return _rangeEncoder.Stream.ErrorCode; + #endif + UInt32 pos, len; + + if (_fastMode) + len = GetOptimumFast(pos); + else + len = GetOptimum(nowPos32, pos); + + UInt32 posState = nowPos32 & _posStateMask; + if(len == 1 && pos == 0xFFFFFFFF) + { + _isMatch[_state.Index][posState].Encode(&_rangeEncoder, 0); + Byte curByte = _matchFinder.GetIndexByte(_matchFinderObj, 0 - _additionalOffset); + CLiteralEncoder2 *subCoder = _literalEncoder.GetSubCoder(nowPos32, _previousByte); + if(_state.IsCharState()) + subCoder->Encode(&_rangeEncoder, curByte); + else + { + Byte matchByte = _matchFinder.GetIndexByte(_matchFinderObj, 0 - _repDistances[0] - 1 - _additionalOffset); + subCoder->EncodeMatched(&_rangeEncoder, matchByte, curByte); + } + _state.UpdateChar(); + _previousByte = curByte; + } + else + { + _isMatch[_state.Index][posState].Encode(&_rangeEncoder, 1); + if(pos < kNumRepDistances) + { + _isRep[_state.Index].Encode(&_rangeEncoder, 1); + if(pos == 0) + { + _isRepG0[_state.Index].Encode(&_rangeEncoder, 0); + _isRep0Long[_state.Index][posState].Encode(&_rangeEncoder, ((len == 1) ? 0 : 1)); + } + else + { + UInt32 distance = _repDistances[pos]; + _isRepG0[_state.Index].Encode(&_rangeEncoder, 1); + if (pos == 1) + _isRepG1[_state.Index].Encode(&_rangeEncoder, 0); + else + { + _isRepG1[_state.Index].Encode(&_rangeEncoder, 1); + _isRepG2[_state.Index].Encode(&_rangeEncoder, pos - 2); + if (pos == 3) + _repDistances[3] = _repDistances[2]; + _repDistances[2] = _repDistances[1]; + } + _repDistances[1] = _repDistances[0]; + _repDistances[0] = distance; + } + if (len == 1) + _state.UpdateShortRep(); + else + { + _repMatchLenEncoder.Encode(&_rangeEncoder, len - kMatchMinLen, posState, !_fastMode); + _state.UpdateRep(); + } + } + else + { + _isRep[_state.Index].Encode(&_rangeEncoder, 0); + _state.UpdateMatch(); + _lenEncoder.Encode(&_rangeEncoder, len - kMatchMinLen, posState, !_fastMode); + pos -= kNumRepDistances; + UInt32 posSlot = GetPosSlot(pos); + _posSlotEncoder[GetLenToPosState(len)].Encode(&_rangeEncoder, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + NRangeCoder::ReverseBitTreeEncode(_posEncoders + base - posSlot - 1, + &_rangeEncoder, footerBits, posReduced); + else + { + _rangeEncoder.EncodeDirectBits(posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + _posAlignEncoder.ReverseEncode(&_rangeEncoder, posReduced & kAlignMask); + _alignPriceCount++; + } + } + _repDistances[3] = _repDistances[2]; + _repDistances[2] = _repDistances[1]; + _repDistances[1] = _repDistances[0]; + _repDistances[0] = pos; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte(_matchFinderObj, len - 1 - _additionalOffset); + } + _additionalOffset -= len; + nowPos32 += len; + if (_additionalOffset == 0) + { + if (!_fastMode) + { + if (_matchPriceCount >= (1 << 7)) + FillDistancesPrices(); + if (_alignPriceCount >= kAlignTableSize) + FillAlignPrices(); + } + if (_matchFinder.GetNumAvailableBytes(_matchFinderObj) == 0) + return Flush(nowPos32); + if (nowPos32 - progressPosValuePrev >= (1 << 14)) + { + nowPos64 += nowPos32 - progressPosValuePrev; + *inSize = nowPos64; + *outSize = _rangeEncoder.GetProcessedSize(); + _finished = false; + *finished = 0; + return _matchFinderBase.result; + } + } + } +} + +STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + #ifndef _NO_EXCEPTIONS + try + { + #endif + return CodeReal(inStream, outStream, inSize, outSize, progress); + #ifndef _NO_EXCEPTIONS + } + catch(const COutBufferException &e) { return e.ErrorCode; } + catch(...) { return E_FAIL; } + #endif +} + +void CEncoder::FillDistancesPrices() +{ + UInt32 tempPrices[kNumFullDistances]; + for (UInt32 i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot(i); + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = NRangeCoder::ReverseBitTreeGetPrice(_posEncoders + + base - posSlot - 1, footerBits, i - base); + } + + for (UInt32 lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + NRangeCoder::CBitTreeEncoder &encoder = _posSlotEncoder[lenToPosState]; + UInt32 *posSlotPrices = _posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < _distTableSize; posSlot++) + posSlotPrices[posSlot] = encoder.GetPrice(posSlot); + for (posSlot = kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << NRangeCoder::kNumBitPriceShiftBits); + + UInt32 *distancesPrices = _distancesPrices[lenToPosState]; + UInt32 i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot(i)] + tempPrices[i]; + } + _matchPriceCount = 0; +} + +void CEncoder::FillAlignPrices() +{ + for (UInt32 i = 0; i < kAlignTableSize; i++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _alignPriceCount = 0; +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.h new file mode 100644 index 0000000..da15979 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.h @@ -0,0 +1,465 @@ +// LZMA/Encoder.h + +#ifndef __LZMA_ENCODER_H +#define __LZMA_ENCODER_H + +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" + +extern "C" +{ + #include "../../../../C/Alloc.h" + #include "../../../../C/Compress/Lz/MatchFinder.h" + #ifdef COMPRESS_MF_MT + #include "../../../../C/Compress/Lz/MatchFinderMt.h" + #endif +} + +#include "../RangeCoder/RangeCoderBitTree.h" + +#include "LZMA.h" + +namespace NCompress { +namespace NLZMA { + +typedef NRangeCoder::CBitEncoder CMyBitEncoder; + +class CBaseState +{ +protected: + CState _state; + Byte _previousByte; + UInt32 _repDistances[kNumRepDistances]; + void Init() + { + _state.Init(); + _previousByte = 0; + for(UInt32 i = 0 ; i < kNumRepDistances; i++) + _repDistances[i] = 0; + } +}; + +struct COptimal +{ + CState State; + + bool Prev1IsChar; + bool Prev2; + + UInt32 PosPrev2; + UInt32 BackPrev2; + + UInt32 Price; + UInt32 PosPrev; // posNext; + UInt32 BackPrev; + UInt32 Backs[kNumRepDistances]; + void MakeAsChar() { BackPrev = UInt32(-1); Prev1IsChar = false; } + void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; } + bool IsShortRep() { return (BackPrev == 0); } +}; + + +// #define LZMA_LOG_BRANCH + +#if _MSC_VER >= 1400 +// Must give gain in core 2. but slower ~2% on k8. +// #define LZMA_LOG_BSR +#endif + +#ifndef LZMA_LOG_BSR +static const int kNumLogBits = 13; // don't change it ! +extern Byte g_FastPos[]; +#endif +inline UInt32 GetPosSlot(UInt32 pos) +{ + #ifdef LZMA_LOG_BSR + if (pos < 2) + return pos; + unsigned long index; + _BitScanReverse(&index, pos); + return (index + index) + ((pos >> (index - 1)) & 1); + #else + if (pos < (1 << kNumLogBits)) + return g_FastPos[pos]; + if (pos < (1 << (kNumLogBits * 2 - 1))) + return g_FastPos[pos >> (kNumLogBits - 1)] + (kNumLogBits - 1) * 2; + return g_FastPos[pos >> (kNumLogBits - 1) * 2] + (kNumLogBits - 1) * 4; + #endif +} + +inline UInt32 GetPosSlot2(UInt32 pos) +{ + #ifdef LZMA_LOG_BSR + unsigned long index; + _BitScanReverse(&index, pos); + return (index + index) + ((pos >> (index - 1)) & 1); + #else + #ifdef LZMA_LOG_BRANCH + if (pos < (1 << (kNumLogBits + 6))) + return g_FastPos[pos >> 6] + 12; + if (pos < (1 << (kNumLogBits * 2 + 5))) + return g_FastPos[pos >> (kNumLogBits + 5)] + (kNumLogBits + 5) * 2; + return g_FastPos[pos >> (kNumLogBits * 2 + 4)] + (kNumLogBits * 2 + 4) * 2; + #else + // it's faster with VC6-32bit. + UInt32 s = 6 + ((kNumLogBits - 1) & (UInt32)((Int32)(((1 << (kNumLogBits + 6)) - 1) - pos) >> 31)); + return g_FastPos[pos >> s] + (s * 2); + #endif + #endif +} + +const UInt32 kIfinityPrice = 0xFFFFFFF; + +const UInt32 kNumOpts = 1 << 12; + + +class CLiteralEncoder2 +{ + CMyBitEncoder _encoders[0x300]; +public: + void Init() + { + for (int i = 0; i < 0x300; i++) + _encoders[i].Init(); + } + void Encode(NRangeCoder::CEncoder *rangeEncoder, Byte symbol); + void EncodeMatched(NRangeCoder::CEncoder *rangeEncoder, Byte matchByte, Byte symbol); + UInt32 GetPrice(bool matchMode, Byte matchByte, Byte symbol) const; +}; + +class CLiteralEncoder +{ + CLiteralEncoder2 *_coders; + int _numPrevBits; + int _numPosBits; + UInt32 _posMask; +public: + CLiteralEncoder(): _coders(0) {} + ~CLiteralEncoder() { Free(); } + void Free() + { + MyFree(_coders); + _coders = 0; + } + bool Create(int numPosBits, int numPrevBits) + { + if (_coders == 0 || (numPosBits + numPrevBits) != (_numPrevBits + _numPosBits)) + { + Free(); + UInt32 numStates = 1 << (numPosBits + numPrevBits); + _coders = (CLiteralEncoder2 *)MyAlloc(numStates * sizeof(CLiteralEncoder2)); + } + _numPosBits = numPosBits; + _posMask = (1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + return (_coders != 0); + } + void Init() + { + UInt32 numStates = 1 << (_numPrevBits + _numPosBits); + for (UInt32 i = 0; i < numStates; i++) + _coders[i].Init(); + } + CLiteralEncoder2 *GetSubCoder(UInt32 pos, Byte prevByte) + { return &_coders[((pos & _posMask) << _numPrevBits) + (prevByte >> (8 - _numPrevBits))]; } +}; + +namespace NLength { + +class CEncoder +{ + CMyBitEncoder _choice; + CMyBitEncoder _choice2; + NRangeCoder::CBitTreeEncoder _lowCoder[kNumPosStatesEncodingMax]; + NRangeCoder::CBitTreeEncoder _midCoder[kNumPosStatesEncodingMax]; + NRangeCoder::CBitTreeEncoder _highCoder; +public: + void Init(UInt32 numPosStates); + void Encode(NRangeCoder::CEncoder *rangeEncoder, UInt32 symbol, UInt32 posState); + void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32 *prices) const; +}; + +const UInt32 kNumSpecSymbols = kNumLowSymbols + kNumMidSymbols; + +class CPriceTableEncoder: public CEncoder +{ + UInt32 _prices[kNumPosStatesEncodingMax][kNumSymbolsTotal]; + UInt32 _tableSize; + UInt32 _counters[kNumPosStatesEncodingMax]; +public: + void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; } + UInt32 GetPrice(UInt32 symbol, UInt32 posState) const { return _prices[posState][symbol]; } + void UpdateTable(UInt32 posState) + { + SetPrices(posState, _tableSize, _prices[posState]); + _counters[posState] = _tableSize; + } + void UpdateTables(UInt32 numPosStates) + { + for (UInt32 posState = 0; posState < numPosStates; posState++) + UpdateTable(posState); + } + void Encode(NRangeCoder::CEncoder *rangeEncoder, UInt32 symbol, UInt32 posState, bool updatePrice) + { + CEncoder::Encode(rangeEncoder, symbol, posState); + if (updatePrice) + if (--_counters[posState] == 0) + UpdateTable(posState); + } +}; + +} + +typedef struct _CSeqInStream +{ + ISeqInStream SeqInStream; + CMyComPtr RealStream; +} CSeqInStream; + + +class CEncoder : + public ICompressCoder, + public ICompressSetOutStream, + public ICompressSetCoderProperties, + public ICompressWriteCoderProperties, + public CBaseState, + public CMyUnknownImp +{ + NRangeCoder::CEncoder _rangeEncoder; + + IMatchFinder _matchFinder; + void *_matchFinderObj; + + #ifdef COMPRESS_MF_MT + Bool _multiThread; + Bool _mtMode; + CMatchFinderMt _matchFinderMt; + #endif + + CMatchFinder _matchFinderBase; + #ifdef COMPRESS_MF_MT + Byte _pad1[kMtCacheLineDummy]; + #endif + + COptimal _optimum[kNumOpts]; + + CMyBitEncoder _isMatch[kNumStates][NLength::kNumPosStatesEncodingMax]; + CMyBitEncoder _isRep[kNumStates]; + CMyBitEncoder _isRepG0[kNumStates]; + CMyBitEncoder _isRepG1[kNumStates]; + CMyBitEncoder _isRepG2[kNumStates]; + CMyBitEncoder _isRep0Long[kNumStates][NLength::kNumPosStatesEncodingMax]; + + NRangeCoder::CBitTreeEncoder _posSlotEncoder[kNumLenToPosStates]; + + CMyBitEncoder _posEncoders[kNumFullDistances - kEndPosModelIndex]; + NRangeCoder::CBitTreeEncoder _posAlignEncoder; + + NLength::CPriceTableEncoder _lenEncoder; + NLength::CPriceTableEncoder _repMatchLenEncoder; + + CLiteralEncoder _literalEncoder; + + UInt32 _matchDistances[kMatchMaxLen * 2 + 2 + 1]; + + bool _fastMode; + // bool _maxMode; + UInt32 _numFastBytes; + UInt32 _longestMatchLength; + UInt32 _numDistancePairs; + + UInt32 _additionalOffset; + + UInt32 _optimumEndIndex; + UInt32 _optimumCurrentIndex; + + bool _longestMatchWasFound; + + UInt32 _posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + + UInt32 _distancesPrices[kNumLenToPosStates][kNumFullDistances]; + + UInt32 _alignPrices[kAlignTableSize]; + UInt32 _alignPriceCount; + + UInt32 _distTableSize; + + UInt32 _posStateBits; + UInt32 _posStateMask; + UInt32 _numLiteralPosStateBits; + UInt32 _numLiteralContextBits; + + UInt32 _dictionarySize; + + UInt32 _matchPriceCount; + UInt64 nowPos64; + bool _finished; + ISequentialInStream *_inStream; + + CSeqInStream _seqInStream; + + UInt32 _matchFinderCycles; + // int _numSkip + + bool _writeEndMark; + + bool _needReleaseMFStream; + + void ReleaseMatchFinder() + { + _matchFinder.Init = 0; + _seqInStream.RealStream.Release(); + } + + void ReleaseMFStream() + { + if (_matchFinderObj && _needReleaseMFStream) + { + #ifdef COMPRESS_MF_MT + if (_mtMode) + MatchFinderMt_ReleaseStream(&_matchFinderMt); + #endif + _needReleaseMFStream = false; + } + _seqInStream.RealStream.Release(); + } + + UInt32 ReadMatchDistances(UInt32 &numDistancePairs); + + void MovePos(UInt32 num); + UInt32 GetRepLen1Price(CState state, UInt32 posState) const + { + return _isRepG0[state.Index].GetPrice0() + + _isRep0Long[state.Index][posState].GetPrice0(); + } + + UInt32 GetPureRepPrice(UInt32 repIndex, CState state, UInt32 posState) const + { + UInt32 price; + if(repIndex == 0) + { + price = _isRepG0[state.Index].GetPrice0(); + price += _isRep0Long[state.Index][posState].GetPrice1(); + } + else + { + price = _isRepG0[state.Index].GetPrice1(); + if (repIndex == 1) + price += _isRepG1[state.Index].GetPrice0(); + else + { + price += _isRepG1[state.Index].GetPrice1(); + price += _isRepG2[state.Index].GetPrice(repIndex - 2); + } + } + return price; + } + UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, CState state, UInt32 posState) const + { + return _repMatchLenEncoder.GetPrice(len - kMatchMinLen, posState) + + GetPureRepPrice(repIndex, state, posState); + } + /* + UInt32 GetPosLen2Price(UInt32 pos, UInt32 posState) const + { + if (pos >= kNumFullDistances) + return kIfinityPrice; + return _distancesPrices[0][pos] + _lenEncoder.GetPrice(0, posState); + } + UInt32 GetPosLen3Price(UInt32 pos, UInt32 len, UInt32 posState) const + { + UInt32 price; + UInt32 lenToPosState = GetLenToPosState(len); + if (pos < kNumFullDistances) + price = _distancesPrices[lenToPosState][pos]; + else + price = _posSlotPrices[lenToPosState][GetPosSlot2(pos)] + + _alignPrices[pos & kAlignMask]; + return price + _lenEncoder.GetPrice(len - kMatchMinLen, posState); + } + */ + UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState) const + { + UInt32 price; + UInt32 lenToPosState = GetLenToPosState(len); + if (pos < kNumFullDistances) + price = _distancesPrices[lenToPosState][pos]; + else + price = _posSlotPrices[lenToPosState][GetPosSlot2(pos)] + + _alignPrices[pos & kAlignMask]; + return price + _lenEncoder.GetPrice(len - kMatchMinLen, posState); + } + + UInt32 Backward(UInt32 &backRes, UInt32 cur); + UInt32 GetOptimum(UInt32 position, UInt32 &backRes); + UInt32 GetOptimumFast(UInt32 &backRes); + + void FillDistancesPrices(); + void FillAlignPrices(); + + void ReleaseStreams() + { + ReleaseMFStream(); + ReleaseOutStream(); + } + + HRESULT Flush(UInt32 nowPos); + class CCoderReleaser + { + CEncoder *_coder; + public: + CCoderReleaser(CEncoder *coder): _coder(coder) {} + ~CCoderReleaser() { _coder->ReleaseStreams(); } + }; + friend class CCoderReleaser; + + void WriteEndMarker(UInt32 posState); + +public: + CEncoder(); + void SetWriteEndMarkerMode(bool writeEndMarker) + { _writeEndMark= writeEndMarker; } + + HRESULT Create(); + + MY_UNKNOWN_IMP3( + ICompressSetOutStream, + ICompressSetCoderProperties, + ICompressWriteCoderProperties + ) + + HRESULT Init(); + + // ICompressCoder interface + HRESULT SetStreams(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize); + HRESULT CodeOneBlock(UInt64 *inSize, UInt64 *outSize, Int32 *finished); + + HRESULT CodeReal(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + + // ICompressCoder interface + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + + // ICompressSetCoderProperties2 + STDMETHOD(SetCoderProperties)(const PROPID *propIDs, + const PROPVARIANT *properties, UInt32 numProperties); + + // ICompressWriteCoderProperties + STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); + + STDMETHOD(SetOutStream)(ISequentialOutStream *outStream); + STDMETHOD(ReleaseOutStream)(); + + virtual ~CEncoder(); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMARegister.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMARegister.cpp new file mode 100644 index 0000000..bc8f726 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/LZMARegister.cpp @@ -0,0 +1,19 @@ +// LZMARegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterCodec.h" + +#include "LZMADecoder.h" +static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLZMA::CDecoder); } +#ifndef EXTRACT_ONLY +#include "LZMAEncoder.h" +static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLZMA::CEncoder); } +#else +#define CreateCodecOut 0 +#endif + +static CCodecInfo g_CodecInfo = + { CreateCodec, CreateCodecOut, 0x030101, L"LZMA", 1, false }; + +REGISTER_CODEC(LZMA) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.h new file mode 100644 index 0000000..e7fb698 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp new file mode 100644 index 0000000..39fee93 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp @@ -0,0 +1,504 @@ +# Microsoft Developer Studio Project File - Name="AloneLZMA" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=AloneLZMA - Win32 DebugU +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "AloneLZMA.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "AloneLZMA.mak" CFG="AloneLZMA - Win32 DebugU" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "AloneLZMA - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "AloneLZMA - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "AloneLZMA - Win32 ReleaseU" (based on "Win32 (x86) Console Application") +!MESSAGE "AloneLZMA - Win32 DebugU" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "AloneLZMA - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\\" /D "NDEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /FAcs /Yu"StdAfx.h" /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\lzma.exe" /opt:NOWIN98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "AloneLZMA - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\lzma.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "AloneLZMA - Win32 ReleaseU" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ReleaseU" +# PROP BASE Intermediate_Dir "ReleaseU" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "ReleaseU" +# PROP Intermediate_Dir "ReleaseU" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /Yu"StdAfx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\\" /D "NDEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7za2.exe" /opt:NOWIN98 +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\lzma.exe" /opt:NOWIN98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "AloneLZMA - Win32 DebugU" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "DebugU" +# PROP BASE Intermediate_Dir "DebugU" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugU" +# PROP Intermediate_Dir "DebugU" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /D "_MBCS" /Yu"StdAfx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7za2.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\lzma.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "AloneLZMA - Win32 Release" +# Name "AloneLZMA - Win32 Debug" +# Name "AloneLZMA - Win32 ReleaseU" +# Name "AloneLZMA - Win32 DebugU" +# Begin Group "Spec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"StdAfx.h" +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Compress" + +# PROP Default_Filter "" +# Begin Group "LZMA" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\LZMA\LZMA.h +# End Source File +# Begin Source File + +SOURCE=..\LZMA\LZMADecoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\LZMA\LZMADecoder.h +# End Source File +# Begin Source File + +SOURCE=..\LZMA\LZMAEncoder.cpp +# End Source File +# Begin Source File + +SOURCE=..\LZMA\LZMAEncoder.h +# End Source File +# End Group +# Begin Group "RangeCoder" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\RangeCoder\RangeCoder.h +# End Source File +# Begin Source File + +SOURCE=..\RangeCoder\RangeCoderBit.cpp +# End Source File +# Begin Source File + +SOURCE=..\RangeCoder\RangeCoderBit.h +# End Source File +# Begin Source File + +SOURCE=..\RangeCoder\RangeCoderBitTree.h +# End Source File +# Begin Source File + +SOURCE=..\RangeCoder\RangeCoderOpt.h +# End Source File +# End Group +# Begin Group "LZ" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\LZ\LZOutWindow.cpp +# End Source File +# Begin Source File + +SOURCE=..\LZ\LZOutWindow.h +# End Source File +# End Group +# End Group +# Begin Group "Windows" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Synchronization.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Synchronization.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\System.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\System.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Thread.h +# End Source File +# End Group +# Begin Group "Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Common\CommandLineParser.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\CommandLineParser.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\CRC.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Defs.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\Defs.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyCom.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyWindows.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringToInt.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringToInt.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Types.h +# End Source File +# End Group +# Begin Group "7zip Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Common\FileStreams.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FileStreams.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\InBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OutBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\OutBuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamUtils.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\StreamUtils.h +# End Source File +# End Group +# Begin Group "C" + +# PROP Default_Filter "" +# Begin Group "C-Lz" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinder.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinder.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinderMt.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lz\MatchFinderMt.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Threads.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Threads.h +# End Source File +# End Group +# Begin Group "LZMA_C" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lzma\LzmaDecode.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lzma\LzmaDecode.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Lzma\LzmaTypes.h +# End Source File +# End Group +# Begin Group "Branch" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchTypes.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchX86.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Compress\Branch\BranchX86.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\..\C\7zCrc.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\7zCrc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Alloc.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Alloc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\C\Types.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\ICoder.h +# End Source File +# Begin Source File + +SOURCE=.\LzmaAlone.cpp +# End Source File +# Begin Source File + +SOURCE=.\LzmaBench.cpp +# End Source File +# Begin Source File + +SOURCE=.\LzmaBench.h +# End Source File +# Begin Source File + +SOURCE=.\LzmaBenchCon.cpp +# End Source File +# Begin Source File + +SOURCE=.\LzmaBenchCon.h +# End Source File +# Begin Source File + +SOURCE=.\LzmaRam.cpp +# End Source File +# Begin Source File + +SOURCE=.\LzmaRam.h +# End Source File +# Begin Source File + +SOURCE=.\LzmaRamDecode.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\LzmaRamDecode.h +# End Source File +# End Target +# End Project diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw new file mode 100644 index 0000000..d7482d8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "AloneLZMA"=.\AloneLZMA.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp new file mode 100644 index 0000000..5b97fd0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp @@ -0,0 +1,554 @@ +// LzmaAlone.cpp + +#include "StdAfx.h" + +#include "../../../Common/MyWindows.h" +#include "../../../Common/MyInitGuid.h" + +#include + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +#include +#include +#define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) +#else +#define MY_SET_BINARY_MODE(file) +#endif + +#include "../../../Common/CommandLineParser.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/StringToInt.h" + +#include "../../Common/FileStreams.h" +#include "../../Common/StreamUtils.h" + +#include "../LZMA/LZMADecoder.h" +#include "../LZMA/LZMAEncoder.h" + +#include "LzmaBenchCon.h" +#include "LzmaRam.h" + +#ifdef COMPRESS_MF_MT +#include "../../../Windows/System.h" +#endif + +#include "../../MyVersion.h" + +extern "C" +{ +#include "LzmaRamDecode.h" +} + +using namespace NCommandLineParser; + +#ifdef _WIN32 +bool g_IsNT = false; +static inline bool IsItWindowsNT() +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + if (!::GetVersionEx(&versionInfo)) + return false; + return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); +} +#endif + +static const char *kCantAllocate = "Can not allocate memory"; +static const char *kReadError = "Read error"; +static const char *kWriteError = "Write error"; + +namespace NKey { +enum Enum +{ + kHelp1 = 0, + kHelp2, + kMode, + kDictionary, + kFastBytes, + kMatchFinderCycles, + kLitContext, + kLitPos, + kPosBits, + kMatchFinder, + kMultiThread, + kEOS, + kStdIn, + kStdOut, + kFilter86 +}; +} + +static const CSwitchForm kSwitchForms[] = +{ + { L"?", NSwitchType::kSimple, false }, + { L"H", NSwitchType::kSimple, false }, + { L"A", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"D", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"FB", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"MC", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"LC", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"LP", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"PB", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"MF", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"MT", NSwitchType::kUnLimitedPostString, false, 0 }, + { L"EOS", NSwitchType::kSimple, false }, + { L"SI", NSwitchType::kSimple, false }, + { L"SO", NSwitchType::kSimple, false }, + { L"F86", NSwitchType::kPostChar, false, 0, 0, L"+" } +}; + +static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]); + +static void PrintHelp() +{ + fprintf(stderr, "\nUsage: LZMA inputFile outputFile [...]\n" + " e: encode file\n" + " d: decode file\n" + " b: Benchmark\n" + "\n" + " -a{N}: set compression mode - [0, 1], default: 1 (max)\n" + " -d{N}: set dictionary - [0,30], default: 23 (8MB)\n" + " -fb{N}: set number of fast bytes - [5, 273], default: 128\n" + " -mc{N}: set number of cycles for match finder\n" + " -lc{N}: set number of literal context bits - [0, 8], default: 3\n" + " -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" + " -pb{N}: set number of pos bits - [0, 4], default: 2\n" + " -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n" + " -mt{N}: set number of CPU threads\n" + " -eos: write End Of Stream marker\n" + " -si: read data from stdin\n" + " -so: write data to stdout\n" + ); +} + +static void PrintHelpAndExit(const char *s) +{ + fprintf(stderr, "\nError: %s\n\n", s); + PrintHelp(); + throw -1; +} + +static void IncorrectCommand() +{ + PrintHelpAndExit("Incorrect command"); +} + +static void WriteArgumentsToStringList(int numArguments, const char *arguments[], + UStringVector &strings) +{ + for(int i = 1; i < numArguments; i++) + strings.Add(MultiByteToUnicodeString(arguments[i])); +} + +static bool GetNumber(const wchar_t *s, UInt32 &value) +{ + value = 0; + if (MyStringLen(s) == 0) + return false; + const wchar_t *end; + UInt64 res = ConvertStringToUInt64(s, &end); + if (*end != L'\0') + return false; + if (res > 0xFFFFFFFF) + return false; + value = UInt32(res); + return true; +} + +int main2(int n, const char *args[]) +{ + #ifdef _WIN32 + g_IsNT = IsItWindowsNT(); + #endif + + fprintf(stderr, "\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n"); + + if (n == 1) + { + PrintHelp(); + return 0; + } + + bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4); + if (unsupportedTypes) + { + fprintf(stderr, "Unsupported base types. Edit Common/Types.h and recompile"); + return 1; + } + + UStringVector commandStrings; + WriteArgumentsToStringList(n, args, commandStrings); + CParser parser(kNumSwitches); + try + { + parser.ParseStrings(kSwitchForms, commandStrings); + } + catch(...) + { + IncorrectCommand(); + } + + if(parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs) + { + PrintHelp(); + return 0; + } + const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; + + int paramIndex = 0; + if (paramIndex >= nonSwitchStrings.Size()) + IncorrectCommand(); + const UString &command = nonSwitchStrings[paramIndex++]; + + bool dictionaryIsDefined = false; + UInt32 dictionary = (UInt32)-1; + if(parser[NKey::kDictionary].ThereIs) + { + UInt32 dicLog; + if (!GetNumber(parser[NKey::kDictionary].PostStrings[0], dicLog)) + IncorrectCommand(); + dictionary = 1 << dicLog; + dictionaryIsDefined = true; + } + UString mf = L"BT4"; + if (parser[NKey::kMatchFinder].ThereIs) + mf = parser[NKey::kMatchFinder].PostStrings[0]; + + UInt32 numThreads = (UInt32)-1; + + #ifdef COMPRESS_MF_MT + if (parser[NKey::kMultiThread].ThereIs) + { + UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors(); + const UString &s = parser[NKey::kMultiThread].PostStrings[0]; + if (s.IsEmpty()) + numThreads = numCPUs; + else + if (!GetNumber(s, numThreads)) + IncorrectCommand(); + } + #endif + + if (command.CompareNoCase(L"b") == 0) + { + const UInt32 kNumDefaultItereations = 1; + UInt32 numIterations = kNumDefaultItereations; + { + if (paramIndex < nonSwitchStrings.Size()) + if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations)) + numIterations = kNumDefaultItereations; + } + return LzmaBenchCon(stderr, numIterations, numThreads, dictionary); + } + + if (numThreads == (UInt32)-1) + numThreads = 1; + + bool encodeMode = false; + if (command.CompareNoCase(L"e") == 0) + encodeMode = true; + else if (command.CompareNoCase(L"d") == 0) + encodeMode = false; + else + IncorrectCommand(); + + bool stdInMode = parser[NKey::kStdIn].ThereIs; + bool stdOutMode = parser[NKey::kStdOut].ThereIs; + + CMyComPtr inStream; + CInFileStream *inStreamSpec = 0; + if (stdInMode) + { + inStream = new CStdInFileStream; + MY_SET_BINARY_MODE(stdin); + } + else + { + if (paramIndex >= nonSwitchStrings.Size()) + IncorrectCommand(); + const UString &inputName = nonSwitchStrings[paramIndex++]; + inStreamSpec = new CInFileStream; + inStream = inStreamSpec; + if (!inStreamSpec->Open(GetSystemString(inputName))) + { + fprintf(stderr, "\nError: can not open input file %s\n", + (const char *)GetOemString(inputName)); + return 1; + } + } + + CMyComPtr outStream; + COutFileStream *outStreamSpec = NULL; + if (stdOutMode) + { + outStream = new CStdOutFileStream; + MY_SET_BINARY_MODE(stdout); + } + else + { + if (paramIndex >= nonSwitchStrings.Size()) + IncorrectCommand(); + const UString &outputName = nonSwitchStrings[paramIndex++]; + outStreamSpec = new COutFileStream; + outStream = outStreamSpec; + if (!outStreamSpec->Create(GetSystemString(outputName), true)) + { + fprintf(stderr, "\nError: can not open output file %s\n", + (const char *)GetOemString(outputName)); + return 1; + } + } + + if (parser[NKey::kFilter86].ThereIs) + { + // -f86 switch is for x86 filtered mode: BCJ + LZMA. + if (parser[NKey::kEOS].ThereIs || stdInMode) + throw "Can not use stdin in this mode"; + UInt64 fileSize; + inStreamSpec->File.GetLength(fileSize); + if (fileSize > 0xF0000000) + throw "File is too big"; + UInt32 inSize = (UInt32)fileSize; + Byte *inBuffer = 0; + if (inSize != 0) + { + inBuffer = (Byte *)MyAlloc((size_t)inSize); + if (inBuffer == 0) + throw kCantAllocate; + } + + UInt32 processedSize; + if (ReadStream(inStream, inBuffer, (UInt32)inSize, &processedSize) != S_OK) + throw "Can not read"; + if ((UInt32)inSize != processedSize) + throw "Read size error"; + + Byte *outBuffer = 0; + size_t outSizeProcessed; + if (encodeMode) + { + // we allocate 105% of original size for output buffer + size_t outSize = (size_t)fileSize / 20 * 21 + (1 << 16); + if (outSize != 0) + { + outBuffer = (Byte *)MyAlloc((size_t)outSize); + if (outBuffer == 0) + throw kCantAllocate; + } + if (!dictionaryIsDefined) + dictionary = 1 << 23; + int res = LzmaRamEncode(inBuffer, inSize, outBuffer, outSize, &outSizeProcessed, + dictionary, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO); + if (res != 0) + { + fprintf(stderr, "\nEncoder error = %d\n", (int)res); + return 1; + } + } + else + { + size_t outSize; + if (LzmaRamGetUncompressedSize(inBuffer, inSize, &outSize) != 0) + throw "data error"; + if (outSize != 0) + { + outBuffer = (Byte *)MyAlloc(outSize); + if (outBuffer == 0) + throw kCantAllocate; + } + int res = LzmaRamDecompress(inBuffer, inSize, outBuffer, outSize, &outSizeProcessed, malloc, free); + if (res != 0) + throw "LzmaDecoder error"; + } + if (WriteStream(outStream, outBuffer, (UInt32)outSizeProcessed, &processedSize) != S_OK) + throw kWriteError; + MyFree(outBuffer); + MyFree(inBuffer); + return 0; + } + + + UInt64 fileSize; + if (encodeMode) + { + NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder; + CMyComPtr encoder = encoderSpec; + + if (!dictionaryIsDefined) + dictionary = 1 << 23; + + UInt32 posStateBits = 2; + UInt32 litContextBits = 3; // for normal files + // UInt32 litContextBits = 0; // for 32-bit data + UInt32 litPosBits = 0; + // UInt32 litPosBits = 2; // for 32-bit data + UInt32 algorithm = 1; + UInt32 numFastBytes = 128; + UInt32 matchFinderCycles = 16 + numFastBytes / 2; + bool matchFinderCyclesDefined = false; + + bool eos = parser[NKey::kEOS].ThereIs || stdInMode; + + if(parser[NKey::kMode].ThereIs) + if (!GetNumber(parser[NKey::kMode].PostStrings[0], algorithm)) + IncorrectCommand(); + + if(parser[NKey::kFastBytes].ThereIs) + if (!GetNumber(parser[NKey::kFastBytes].PostStrings[0], numFastBytes)) + IncorrectCommand(); + matchFinderCyclesDefined = parser[NKey::kMatchFinderCycles].ThereIs; + if (matchFinderCyclesDefined) + if (!GetNumber(parser[NKey::kMatchFinderCycles].PostStrings[0], matchFinderCycles)) + IncorrectCommand(); + if(parser[NKey::kLitContext].ThereIs) + if (!GetNumber(parser[NKey::kLitContext].PostStrings[0], litContextBits)) + IncorrectCommand(); + if(parser[NKey::kLitPos].ThereIs) + if (!GetNumber(parser[NKey::kLitPos].PostStrings[0], litPosBits)) + IncorrectCommand(); + if(parser[NKey::kPosBits].ThereIs) + if (!GetNumber(parser[NKey::kPosBits].PostStrings[0], posStateBits)) + IncorrectCommand(); + + PROPID propIDs[] = + { + NCoderPropID::kDictionarySize, + NCoderPropID::kPosStateBits, + NCoderPropID::kLitContextBits, + NCoderPropID::kLitPosBits, + NCoderPropID::kAlgorithm, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinder, + NCoderPropID::kEndMarker, + NCoderPropID::kNumThreads, + NCoderPropID::kMatchFinderCycles, + }; + const int kNumPropsMax = sizeof(propIDs) / sizeof(propIDs[0]); + + PROPVARIANT properties[kNumPropsMax]; + for (int p = 0; p < 6; p++) + properties[p].vt = VT_UI4; + + properties[0].ulVal = (UInt32)dictionary; + properties[1].ulVal = (UInt32)posStateBits; + properties[2].ulVal = (UInt32)litContextBits; + properties[3].ulVal = (UInt32)litPosBits; + properties[4].ulVal = (UInt32)algorithm; + properties[5].ulVal = (UInt32)numFastBytes; + + properties[6].vt = VT_BSTR; + properties[6].bstrVal = (BSTR)(const wchar_t *)mf; + + properties[7].vt = VT_BOOL; + properties[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE; + + properties[8].vt = VT_UI4; + properties[8].ulVal = (UInt32)numThreads; + + // it must be last in property list + properties[9].vt = VT_UI4; + properties[9].ulVal = (UInt32)matchFinderCycles; + + int numProps = kNumPropsMax; + if (!matchFinderCyclesDefined) + numProps--; + + if (encoderSpec->SetCoderProperties(propIDs, properties, numProps) != S_OK) + IncorrectCommand(); + encoderSpec->WriteCoderProperties(outStream); + + if (eos || stdInMode) + fileSize = (UInt64)(Int64)-1; + else + inStreamSpec->File.GetLength(fileSize); + + for (int i = 0; i < 8; i++) + { + Byte b = Byte(fileSize >> (8 * i)); + if (outStream->Write(&b, 1, 0) != S_OK) + { + fprintf(stderr, kWriteError); + return 1; + } + } + HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0); + if (result == E_OUTOFMEMORY) + { + fprintf(stderr, "\nError: Can not allocate memory\n"); + return 1; + } + else if (result != S_OK) + { + fprintf(stderr, "\nEncoder error = %X\n", (unsigned int)result); + return 1; + } + } + else + { + NCompress::NLZMA::CDecoder *decoderSpec = new NCompress::NLZMA::CDecoder; + CMyComPtr decoder = decoderSpec; + const UInt32 kPropertiesSize = 5; + Byte properties[kPropertiesSize]; + UInt32 processedSize; + if (ReadStream(inStream, properties, kPropertiesSize, &processedSize) != S_OK) + { + fprintf(stderr, kReadError); + return 1; + } + if (processedSize != kPropertiesSize) + { + fprintf(stderr, kReadError); + return 1; + } + if (decoderSpec->SetDecoderProperties2(properties, kPropertiesSize) != S_OK) + { + fprintf(stderr, "SetDecoderProperties error"); + return 1; + } + fileSize = 0; + for (int i = 0; i < 8; i++) + { + Byte b; + if (inStream->Read(&b, 1, &processedSize) != S_OK) + { + fprintf(stderr, kReadError); + return 1; + } + if (processedSize != 1) + { + fprintf(stderr, kReadError); + return 1; + } + fileSize |= ((UInt64)b) << (8 * i); + } + if (decoder->Code(inStream, outStream, 0, &fileSize, 0) != S_OK) + { + fprintf(stderr, "Decoder error"); + return 1; + } + } + if (outStreamSpec != NULL) + { + if (outStreamSpec->Close() != S_OK) + { + fprintf(stderr, "File closing error"); + return 1; + } + } + return 0; +} + +int main(int n, const char *args[]) +{ + try { return main2(n, args); } + catch(const char *s) + { + fprintf(stderr, "\nError: %s\n", s); + return 1; + } + catch(...) + { + fprintf(stderr, "\nError\n"); + return 1; + } +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp new file mode 100644 index 0000000..4cd2d63 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp @@ -0,0 +1,1024 @@ +// LzmaBench.cpp + +#include "StdAfx.h" + +#include "LzmaBench.h" + +#ifndef _WIN32 +#define USE_POSIX_TIME +#define USE_POSIX_TIME2 +#endif + +#ifdef USE_POSIX_TIME +#include +#ifdef USE_POSIX_TIME2 +#include +#endif +#endif + +#ifdef _WIN32 +#define USE_ALLOCA +#endif + +#ifdef USE_ALLOCA +#ifdef _WIN32 +#include +#else +#include +#endif +#endif + +extern "C" +{ +#include "../../../../C/Alloc.h" +#include "../../../../C/7zCrc.h" +} +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" + +#ifdef BENCH_MT +#include "../../../Windows/Thread.h" +#include "../../../Windows/Synchronization.h" +#endif + +#ifdef EXTERNAL_LZMA +#include "../../../Windows/PropVariant.h" +#else +#include "../LZMA/LZMADecoder.h" +#include "../LZMA/LZMAEncoder.h" +#endif + +static const UInt32 kUncompressMinBlockSize = 1 << 26; +static const UInt32 kAdditionalSize = (1 << 16); +static const UInt32 kCompressedAdditionalSize = (1 << 10); +static const UInt32 kMaxLzmaPropSize = 5; + +class CBaseRandomGenerator +{ + UInt32 A1; + UInt32 A2; +public: + CBaseRandomGenerator() { Init(); } + void Init() { A1 = 362436069; A2 = 521288629;} + UInt32 GetRnd() + { + return + ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) + + ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) ); + } +}; + +class CBenchBuffer +{ +public: + size_t BufferSize; + Byte *Buffer; + CBenchBuffer(): Buffer(0) {} + virtual ~CBenchBuffer() { Free(); } + void Free() + { + ::MidFree(Buffer); + Buffer = 0; + } + bool Alloc(size_t bufferSize) + { + if (Buffer != 0 && BufferSize == bufferSize) + return true; + Free(); + Buffer = (Byte *)::MidAlloc(bufferSize); + BufferSize = bufferSize; + return (Buffer != 0); + } +}; + +class CBenchRandomGenerator: public CBenchBuffer +{ + CBaseRandomGenerator *RG; +public: + void Set(CBaseRandomGenerator *rg) { RG = rg; } + UInt32 GetVal(UInt32 &res, int numBits) + { + UInt32 val = res & (((UInt32)1 << numBits) - 1); + res >>= numBits; + return val; + } + UInt32 GetLen(UInt32 &res) + { + UInt32 len = GetVal(res, 2); + return GetVal(res, 1 + len); + } + void Generate() + { + UInt32 pos = 0; + UInt32 rep0 = 1; + while (pos < BufferSize) + { + UInt32 res = RG->GetRnd(); + res >>= 1; + if (GetVal(res, 1) == 0 || pos < 1024) + Buffer[pos++] = (Byte)(res & 0xFF); + else + { + UInt32 len; + len = 1 + GetLen(res); + if (GetVal(res, 3) != 0) + { + len += GetLen(res); + do + { + UInt32 ppp = GetVal(res, 5) + 6; + res = RG->GetRnd(); + if (ppp > 30) + continue; + rep0 = /* (1 << ppp) +*/ GetVal(res, ppp); + res = RG->GetRnd(); + } + while (rep0 >= pos); + rep0++; + } + + for (UInt32 i = 0; i < len && pos < BufferSize; i++, pos++) + Buffer[pos] = Buffer[pos - rep0]; + } + } + } +}; + + +class CBenchmarkInStream: + public ISequentialInStream, + public CMyUnknownImp +{ + const Byte *Data; + size_t Pos; + size_t Size; +public: + MY_UNKNOWN_IMP + void Init(const Byte *data, size_t size) + { + Data = data; + Size = size; + Pos = 0; + } + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + size_t remain = Size - Pos; + UInt32 kMaxBlockSize = (1 << 20); + if (size > kMaxBlockSize) + size = kMaxBlockSize; + if (size > remain) + size = (UInt32)remain; + for (UInt32 i = 0; i < size; i++) + ((Byte *)data)[i] = Data[Pos + i]; + Pos += size; + if(processedSize != NULL) + *processedSize = size; + return S_OK; +} + +class CBenchmarkOutStream: + public ISequentialOutStream, + public CBenchBuffer, + public CMyUnknownImp +{ + // bool _overflow; +public: + UInt32 Pos; + // CBenchmarkOutStream(): _overflow(false) {} + void Init() + { + // _overflow = false; + Pos = 0; + } + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + size_t curSize = BufferSize - Pos; + if (curSize > size) + curSize = size; + memcpy(Buffer + Pos, data, curSize); + Pos += (UInt32)curSize; + if(processedSize != NULL) + *processedSize = (UInt32)curSize; + if (curSize != size) + { + // _overflow = true; + return E_FAIL; + } + return S_OK; +} + +class CCrcOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + UInt32 Crc; + MY_UNKNOWN_IMP + void Init() { Crc = CRC_INIT_VAL; } + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + Crc = CrcUpdate(Crc, data, size); + if (processedSize != NULL) + *processedSize = size; + return S_OK; +} + +static UInt64 GetTimeCount() +{ + #ifdef USE_POSIX_TIME + #ifdef USE_POSIX_TIME2 + timeval v; + if (gettimeofday(&v, 0) == 0) + return (UInt64)(v.tv_sec) * 1000000 + v.tv_usec; + return (UInt64)time(NULL) * 1000000; + #else + return time(NULL); + #endif + #else + /* + LARGE_INTEGER value; + if (::QueryPerformanceCounter(&value)) + return value.QuadPart; + */ + return GetTickCount(); + #endif +} + +static UInt64 GetFreq() +{ + #ifdef USE_POSIX_TIME + #ifdef USE_POSIX_TIME2 + return 1000000; + #else + return 1; + #endif + #else + /* + LARGE_INTEGER value; + if (::QueryPerformanceFrequency(&value)) + return value.QuadPart; + */ + return 1000; + #endif +} + +#ifndef USE_POSIX_TIME +static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; } +#endif +static UInt64 GetUserTime() +{ + #ifdef USE_POSIX_TIME + return clock(); + #else + FILETIME creationTime, exitTime, kernelTime, userTime; + if (::GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime) != 0) + return GetTime64(userTime) + GetTime64(kernelTime); + return (UInt64)GetTickCount() * 10000; + #endif +} + +static UInt64 GetUserFreq() +{ + #ifdef USE_POSIX_TIME + return CLOCKS_PER_SEC; + #else + return 10000000; + #endif +} + +class CBenchProgressStatus +{ + #ifdef BENCH_MT + NWindows::NSynchronization::CCriticalSection CS; + #endif +public: + HRESULT Res; + bool EncodeMode; + void SetResult(HRESULT res) + { + #ifdef BENCH_MT + NWindows::NSynchronization::CCriticalSectionLock lock(CS); + #endif + Res = res; + } + HRESULT GetResult() + { + #ifdef BENCH_MT + NWindows::NSynchronization::CCriticalSectionLock lock(CS); + #endif + return Res; + } +}; + +class CBenchProgressInfo: + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + CBenchProgressStatus *Status; + CBenchInfo BenchInfo; + HRESULT Res; + IBenchCallback *callback; + CBenchProgressInfo(): callback(0) {} + MY_UNKNOWN_IMP + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +void SetStartTime(CBenchInfo &bi) +{ + bi.GlobalFreq = GetFreq(); + bi.UserFreq = GetUserFreq(); + bi.GlobalTime = ::GetTimeCount(); + bi.UserTime = ::GetUserTime(); +} + +void SetFinishTime(const CBenchInfo &biStart, CBenchInfo &dest) +{ + dest.GlobalFreq = GetFreq(); + dest.UserFreq = GetUserFreq(); + dest.GlobalTime = ::GetTimeCount() - biStart.GlobalTime; + dest.UserTime = ::GetUserTime() - biStart.UserTime; +} + +STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + HRESULT res = Status->GetResult(); + if (res != S_OK) + return res; + if (!callback) + return res; + CBenchInfo info = BenchInfo; + SetFinishTime(BenchInfo, info); + if (Status->EncodeMode) + { + info.UnpackSize = *inSize; + info.PackSize = *outSize; + res = callback->SetEncodeResult(info, false); + } + else + { + info.PackSize = BenchInfo.PackSize + *inSize; + info.UnpackSize = BenchInfo.UnpackSize + *outSize; + res = callback->SetDecodeResult(info, false); + } + if (res != S_OK) + Status->SetResult(res); + return res; +} + +static const int kSubBits = 8; + +static UInt32 GetLogSize(UInt32 size) +{ + for (int i = kSubBits; i < 32; i++) + for (UInt32 j = 0; j < (1 << kSubBits); j++) + if (size <= (((UInt32)1) << i) + (j << (i - kSubBits))) + return (i << kSubBits) + j; + return (32 << kSubBits); +} + +static void NormalizeVals(UInt64 &v1, UInt64 &v2) +{ + while (v1 > 1000000) + { + v1 >>= 1; + v2 >>= 1; + } +} + +UInt64 GetUsage(const CBenchInfo &info) +{ + UInt64 userTime = info.UserTime; + UInt64 userFreq = info.UserFreq; + UInt64 globalTime = info.GlobalTime; + UInt64 globalFreq = info.GlobalFreq; + NormalizeVals(userTime, userFreq); + NormalizeVals(globalFreq, globalTime); + if (userFreq == 0) + userFreq = 1; + if (globalTime == 0) + globalTime = 1; + return userTime * globalFreq * 1000000 / userFreq / globalTime; +} + +UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating) +{ + UInt64 userTime = info.UserTime; + UInt64 userFreq = info.UserFreq; + UInt64 globalTime = info.GlobalTime; + UInt64 globalFreq = info.GlobalFreq; + NormalizeVals(userFreq, userTime); + NormalizeVals(globalTime, globalFreq); + if (globalFreq == 0) + globalFreq = 1; + if (userTime == 0) + userTime = 1; + return userFreq * globalTime / globalFreq * rating / userTime; +} + +static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq) +{ + UInt64 elTime = elapsedTime; + NormalizeVals(freq, elTime); + if (elTime == 0) + elTime = 1; + return value * freq / elTime; +} + +UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size) +{ + UInt64 t = GetLogSize(dictionarySize) - (kBenchMinDicLogSize << kSubBits); + // UInt64 numCommandsForOne = 1000 + ((t * t * 7) >> (2 * kSubBits)); // AMD K8 + UInt64 numCommandsForOne = 870 + ((t * t * 5) >> (2 * kSubBits)); // Intel Core2 + + UInt64 numCommands = (UInt64)(size) * numCommandsForOne; + return MyMultDiv64(numCommands, elapsedTime, freq); +} + +UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations) +{ + // UInt64 numCommands = (inSize * 216 + outSize * 14) * numIterations; // AMD K8 + UInt64 numCommands = (inSize * 220 + outSize * 8) * numIterations; // Intel Core2 + return MyMultDiv64(numCommands, elapsedTime, freq); +} + +#ifdef EXTERNAL_LZMA +typedef UInt32 (WINAPI * CreateObjectPointer)(const GUID *clsID, + const GUID *interfaceID, void **outObject); +#endif + +struct CEncoderInfo; + +struct CEncoderInfo +{ + #ifdef BENCH_MT + NWindows::CThread thread[2]; + #endif + CMyComPtr encoder; + CBenchProgressInfo *progressInfoSpec[2]; + CMyComPtr progressInfo[2]; + UInt32 NumIterations; + #ifdef USE_ALLOCA + size_t AllocaSize; + #endif + + struct CDecoderInfo + { + CEncoderInfo *Encoder; + UInt32 DecoderIndex; + #ifdef USE_ALLOCA + size_t AllocaSize; + #endif + bool CallbackMode; + }; + CDecoderInfo decodersInfo[2]; + + CMyComPtr decoders[2]; + HRESULT Results[2]; + CBenchmarkOutStream *outStreamSpec; + CMyComPtr outStream; + IBenchCallback *callback; + UInt32 crc; + UInt32 kBufferSize; + UInt32 compressedSize; + CBenchRandomGenerator rg; + CBenchmarkOutStream *propStreamSpec; + CMyComPtr propStream; + HRESULT Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rg); + HRESULT Encode(); + HRESULT Decode(UInt32 decoderIndex); + + CEncoderInfo(): outStreamSpec(0), callback(0), propStreamSpec(0) {} + + #ifdef BENCH_MT + static THREAD_FUNC_DECL EncodeThreadFunction(void *param) + { + CEncoderInfo *encoder = (CEncoderInfo *)param; + #ifdef USE_ALLOCA + alloca(encoder->AllocaSize); + #endif + HRESULT res = encoder->Encode(); + encoder->Results[0] = res; + if (res != S_OK) + encoder->progressInfoSpec[0]->Status->SetResult(res); + + return 0; + } + static THREAD_FUNC_DECL DecodeThreadFunction(void *param) + { + CDecoderInfo *decoder = (CDecoderInfo *)param; + #ifdef USE_ALLOCA + alloca(decoder->AllocaSize); + #endif + CEncoderInfo *encoder = decoder->Encoder; + encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex); + return 0; + } + + HRESULT CreateEncoderThread() + { + return thread[0].Create(EncodeThreadFunction, this); + } + + HRESULT CreateDecoderThread(int index, bool callbackMode + #ifdef USE_ALLOCA + , size_t allocaSize + #endif + ) + { + CDecoderInfo &decoder = decodersInfo[index]; + decoder.DecoderIndex = index; + decoder.Encoder = this; + #ifdef USE_ALLOCA + decoder.AllocaSize = allocaSize; + #endif + decoder.CallbackMode = callbackMode; + return thread[index].Create(DecodeThreadFunction, &decoder); + } + #endif +}; + +HRESULT CEncoderInfo::Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rgLoc) +{ + rg.Set(rgLoc); + kBufferSize = dictionarySize + kAdditionalSize; + UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize; + if (!rg.Alloc(kBufferSize)) + return E_OUTOFMEMORY; + rg.Generate(); + crc = CrcCalc(rg.Buffer, rg.BufferSize); + + outStreamSpec = new CBenchmarkOutStream; + if (!outStreamSpec->Alloc(kCompressedBufferSize)) + return E_OUTOFMEMORY; + + outStream = outStreamSpec; + + propStreamSpec = 0; + if (!propStream) + { + propStreamSpec = new CBenchmarkOutStream; + propStream = propStreamSpec; + } + if (!propStreamSpec->Alloc(kMaxLzmaPropSize)) + return E_OUTOFMEMORY; + propStreamSpec->Init(); + + PROPID propIDs[] = + { + NCoderPropID::kDictionarySize, + NCoderPropID::kMultiThread + }; + const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]); + PROPVARIANT properties[kNumProps]; + properties[0].vt = VT_UI4; + properties[0].ulVal = (UInt32)dictionarySize; + + properties[1].vt = VT_BOOL; + properties[1].boolVal = (numThreads > 1) ? VARIANT_TRUE : VARIANT_FALSE; + + { + CMyComPtr setCoderProperties; + RINOK(encoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties)); + if (!setCoderProperties) + return E_FAIL; + RINOK(setCoderProperties->SetCoderProperties(propIDs, properties, kNumProps)); + + CMyComPtr writeCoderProperties; + encoder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProperties); + if (writeCoderProperties) + { + RINOK(writeCoderProperties->WriteCoderProperties(propStream)); + } + } + return S_OK; +} + +HRESULT CEncoderInfo::Encode() +{ + CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream; + CMyComPtr inStream = inStreamSpec; + inStreamSpec->Init(rg.Buffer, rg.BufferSize); + outStreamSpec->Init(); + + RINOK(encoder->Code(inStream, outStream, 0, 0, progressInfo[0])); + compressedSize = outStreamSpec->Pos; + encoder.Release(); + return S_OK; +} + +HRESULT CEncoderInfo::Decode(UInt32 decoderIndex) +{ + CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream; + CMyComPtr inStream = inStreamSpec; + CMyComPtr &decoder = decoders[decoderIndex]; + + CMyComPtr compressSetDecoderProperties; + decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &compressSetDecoderProperties); + if (!compressSetDecoderProperties) + return E_FAIL; + + CCrcOutStream *crcOutStreamSpec = new CCrcOutStream; + CMyComPtr crcOutStream = crcOutStreamSpec; + + CBenchProgressInfo *pi = progressInfoSpec[decoderIndex]; + pi->BenchInfo.UnpackSize = 0; + pi->BenchInfo.PackSize = 0; + + for (UInt32 j = 0; j < NumIterations; j++) + { + inStreamSpec->Init(outStreamSpec->Buffer, compressedSize); + crcOutStreamSpec->Init(); + + RINOK(compressSetDecoderProperties->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos)); + UInt64 outSize = kBufferSize; + RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex])); + if (CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc) + return S_FALSE; + pi->BenchInfo.UnpackSize += kBufferSize; + pi->BenchInfo.PackSize += compressedSize; + } + decoder.Release(); + return S_OK; +} + +static const UInt32 kNumThreadsMax = (1 << 16); + +struct CBenchEncoders +{ + CEncoderInfo *encoders; + CBenchEncoders(UInt32 num): encoders(0) { encoders = new CEncoderInfo[num]; } + ~CBenchEncoders() { delete []encoders; } +}; + +HRESULT LzmaBench( + #ifdef EXTERNAL_LZMA + CCodecs *codecs, + #endif + UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback) +{ + UInt32 numEncoderThreads = + #ifdef BENCH_MT + (numThreads > 1 ? numThreads / 2 : 1); + #else + 1; + #endif + UInt32 numSubDecoderThreads = + #ifdef BENCH_MT + (numThreads > 1 ? 2 : 1); + #else + 1; + #endif + if (dictionarySize < (1 << kBenchMinDicLogSize) || numThreads < 1 || numEncoderThreads > kNumThreadsMax) + { + return E_INVALIDARG; + } + + CBenchEncoders encodersSpec(numEncoderThreads); + CEncoderInfo *encoders = encodersSpec.encoders; + + #ifdef EXTERNAL_LZMA + UString name = L"LZMA"; + #endif + + UInt32 i; + for (i = 0; i < numEncoderThreads; i++) + { + CEncoderInfo &encoder = encoders[i]; + encoder.callback = (i == 0) ? callback : 0; + + #ifdef EXTERNAL_LZMA + RINOK(codecs->CreateCoder(name, true, encoder.encoder)); + #else + encoder.encoder = new NCompress::NLZMA::CEncoder; + #endif + for (UInt32 j = 0; j < numSubDecoderThreads; j++) + { + #ifdef EXTERNAL_LZMA + RINOK(codecs->CreateCoder(name, false, encoder.decoders[j])); + #else + encoder.decoders[j] = new NCompress::NLZMA::CDecoder; + #endif + } + } + + CBaseRandomGenerator rg; + rg.Init(); + for (i = 0; i < numEncoderThreads; i++) + { + RINOK(encoders[i].Init(dictionarySize, numThreads, &rg)); + } + + CBenchProgressStatus status; + status.Res = S_OK; + status.EncodeMode = true; + + for (i = 0; i < numEncoderThreads; i++) + { + CEncoderInfo &encoder = encoders[i]; + for (int j = 0; j < 2; j++) + { + encoder.progressInfo[j] = encoder.progressInfoSpec[j] = new CBenchProgressInfo; + encoder.progressInfoSpec[j]->Status = &status; + } + if (i == 0) + { + encoder.progressInfoSpec[0]->callback = callback; + encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numEncoderThreads; + SetStartTime(encoder.progressInfoSpec[0]->BenchInfo); + } + + #ifdef BENCH_MT + if (numEncoderThreads > 1) + { + #ifdef USE_ALLOCA + encoder.AllocaSize = (i * 16 * 21) & 0x7FF; + #endif + RINOK(encoder.CreateEncoderThread()) + } + else + #endif + { + RINOK(encoder.Encode()); + } + } + #ifdef BENCH_MT + if (numEncoderThreads > 1) + for (i = 0; i < numEncoderThreads; i++) + encoders[i].thread[0].Wait(); + #endif + + RINOK(status.Res); + + CBenchInfo info; + + SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info); + info.UnpackSize = 0; + info.PackSize = 0; + info.NumIterations = 1; // progressInfoSpec->NumIterations; + for (i = 0; i < numEncoderThreads; i++) + { + CEncoderInfo &encoder = encoders[i]; + info.UnpackSize += encoder.kBufferSize; + info.PackSize += encoder.compressedSize; + } + RINOK(callback->SetEncodeResult(info, true)); + + + status.Res = S_OK; + status.EncodeMode = false; + + UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads; + for (i = 0; i < numEncoderThreads; i++) + { + CEncoderInfo &encoder = encoders[i]; + encoder.NumIterations = 2 + kUncompressMinBlockSize / encoder.kBufferSize; + + if (i == 0) + { + encoder.progressInfoSpec[0]->callback = callback; + encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numDecoderThreads; + SetStartTime(encoder.progressInfoSpec[0]->BenchInfo); + } + + #ifdef BENCH_MT + if (numDecoderThreads > 1) + { + for (UInt32 j = 0; j < numSubDecoderThreads; j++) + { + size_t allocaSize = ((i * numSubDecoderThreads + j) * 16 * 21) & 0x7FF; + HRESULT res = encoder.CreateDecoderThread(j, (i == 0 && j == 0) + #ifdef USE_ALLOCA + , allocaSize + #endif + ); + RINOK(res); + } + } + else + #endif + { + RINOK(encoder.Decode(0)); + } + } + #ifdef BENCH_MT + HRESULT res = S_OK; + if (numDecoderThreads > 1) + for (i = 0; i < numEncoderThreads; i++) + for (UInt32 j = 0; j < numSubDecoderThreads; j++) + { + CEncoderInfo &encoder = encoders[i]; + encoder.thread[j].Wait(); + if (encoder.Results[j] != S_OK) + res = encoder.Results[j]; + } + RINOK(res); + #endif + RINOK(status.Res); + SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info); + info.UnpackSize = 0; + info.PackSize = 0; + info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations; + for (i = 0; i < numEncoderThreads; i++) + { + CEncoderInfo &encoder = encoders[i]; + info.UnpackSize += encoder.kBufferSize; + info.PackSize += encoder.compressedSize; + } + RINOK(callback->SetDecodeResult(info, false)); + RINOK(callback->SetDecodeResult(info, true)); + return S_OK; +} + + +inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary) +{ + UInt32 hs = dictionary - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) + hs >>= 1; + hs++; + return ((hs + (1 << 16)) + (UInt64)dictionary * 2) * 4 + (UInt64)dictionary * 3 / 2 + + (1 << 20) + (multiThread ? (6 << 20) : 0); +} + +UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary) +{ + const UInt32 kBufferSize = dictionary; + const UInt32 kCompressedBufferSize = (kBufferSize / 2); + UInt32 numSubThreads = (numThreads > 1) ? 2 : 1; + UInt32 numBigThreads = numThreads / numSubThreads; + return (kBufferSize + kCompressedBufferSize + + GetLZMAUsage((numThreads > 1), dictionary) + (2 << 20)) * numBigThreads; +} + +static bool CrcBig(const void *data, UInt32 size, UInt32 numCycles, UInt32 crcBase) +{ + for (UInt32 i = 0; i < numCycles; i++) + if (CrcCalc(data, size) != crcBase) + return false; + return true; +} + +#ifdef BENCH_MT +struct CCrcInfo +{ + NWindows::CThread Thread; + const Byte *Data; + UInt32 Size; + UInt32 NumCycles; + UInt32 Crc; + bool Res; + void Wait() + { + Thread.Wait(); + Thread.Close(); + } +}; + +static THREAD_FUNC_DECL CrcThreadFunction(void *param) +{ + CCrcInfo *p = (CCrcInfo *)param; + p->Res = CrcBig(p->Data, p->Size, p->NumCycles, p->Crc); + return 0; +} + +struct CCrcThreads +{ + UInt32 NumThreads; + CCrcInfo *Items; + CCrcThreads(): Items(0), NumThreads(0) {} + void WaitAll() + { + for (UInt32 i = 0; i < NumThreads; i++) + Items[i].Wait(); + NumThreads = 0; + } + ~CCrcThreads() + { + WaitAll(); + delete []Items; + } +}; +#endif + +static UInt32 CrcCalc1(const Byte *buf, UInt32 size) +{ + UInt32 crc = CRC_INIT_VAL;; + for (UInt32 i = 0; i < size; i++) + crc = CRC_UPDATE_BYTE(crc, buf[i]); + return CRC_GET_DIGEST(crc); +} + +static void RandGen(Byte *buf, UInt32 size, CBaseRandomGenerator &RG) +{ + for (UInt32 i = 0; i < size; i++) + buf[i] = (Byte)RG.GetRnd(); +} + +static UInt32 RandGenCrc(Byte *buf, UInt32 size, CBaseRandomGenerator &RG) +{ + RandGen(buf, size, RG); + return CrcCalc1(buf, size); +} + +bool CrcInternalTest() +{ + CBenchBuffer buffer; + const UInt32 kBufferSize0 = (1 << 8); + const UInt32 kBufferSize1 = (1 << 10); + const UInt32 kCheckSize = (1 << 5); + if (!buffer.Alloc(kBufferSize0 + kBufferSize1)) + return false; + Byte *buf = buffer.Buffer; + UInt32 i; + for (i = 0; i < kBufferSize0; i++) + buf[i] = (Byte)i; + UInt32 crc1 = CrcCalc1(buf, kBufferSize0); + if (crc1 != 0x29058C73) + return false; + CBaseRandomGenerator RG; + RandGen(buf + kBufferSize0, kBufferSize1, RG); + for (i = 0; i < kBufferSize0 + kBufferSize1 - kCheckSize; i++) + for (UInt32 j = 0; j < kCheckSize; j++) + if (CrcCalc1(buf + i, j) != CrcCalc(buf + i, j)) + return false; + return true; +} + +HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed) +{ + if (numThreads == 0) + numThreads = 1; + + CBenchBuffer buffer; + size_t totalSize = (size_t)bufferSize * numThreads; + if (totalSize / numThreads != bufferSize) + return E_OUTOFMEMORY; + if (!buffer.Alloc(totalSize)) + return E_OUTOFMEMORY; + + Byte *buf = buffer.Buffer; + CBaseRandomGenerator RG; + UInt32 numCycles = ((UInt32)1 << 30) / ((bufferSize >> 2) + 1) + 1; + + UInt64 timeVal; + #ifdef BENCH_MT + CCrcThreads threads; + if (numThreads > 1) + { + threads.Items = new CCrcInfo[numThreads]; + UInt32 i; + for (i = 0; i < numThreads; i++) + { + CCrcInfo &info = threads.Items[i]; + Byte *data = buf + (size_t)bufferSize * i; + info.Data = data; + info.NumCycles = numCycles; + info.Size = bufferSize; + info.Crc = RandGenCrc(data, bufferSize, RG); + } + timeVal = GetTimeCount(); + for (i = 0; i < numThreads; i++) + { + CCrcInfo &info = threads.Items[i]; + RINOK(info.Thread.Create(CrcThreadFunction, &info)); + threads.NumThreads++; + } + threads.WaitAll(); + for (i = 0; i < numThreads; i++) + if (!threads.Items[i].Res) + return S_FALSE; + } + else + #endif + { + UInt32 crc = RandGenCrc(buf, bufferSize, RG); + timeVal = GetTimeCount(); + if (!CrcBig(buf, bufferSize, numCycles, crc)) + return S_FALSE; + } + timeVal = GetTimeCount() - timeVal; + if (timeVal == 0) + timeVal = 1; + + UInt64 size = (UInt64)numCycles * totalSize; + speed = MyMultDiv64(size, timeVal, GetFreq()); + return S_OK; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h new file mode 100644 index 0000000..d57e797 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h @@ -0,0 +1,48 @@ +// LzmaBench.h + +#ifndef __LZMABENCH_H +#define __LZMABENCH_H + +#include +#include "../../../Common/Types.h" +#ifdef EXTERNAL_LZMA +#include "../../UI/Common/LoadCodecs.h" +#endif + +struct CBenchInfo +{ + UInt64 GlobalTime; + UInt64 GlobalFreq; + UInt64 UserTime; + UInt64 UserFreq; + UInt64 UnpackSize; + UInt64 PackSize; + UInt32 NumIterations; + CBenchInfo(): NumIterations(0) {} +}; + +struct IBenchCallback +{ + virtual HRESULT SetEncodeResult(const CBenchInfo &info, bool final) = 0; + virtual HRESULT SetDecodeResult(const CBenchInfo &info, bool final) = 0; +}; + +UInt64 GetUsage(const CBenchInfo &benchOnfo); +UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating); +UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size); +UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations); + +HRESULT LzmaBench( + #ifdef EXTERNAL_LZMA + CCodecs *codecs, + #endif + UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback); + +const int kBenchMinDicLogSize = 18; + +UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary); + +bool CrcInternalTest(); +HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp new file mode 100644 index 0000000..e55b4bc --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp @@ -0,0 +1,311 @@ +// LzmaBenchCon.cpp + +#include "StdAfx.h" + +#include + +#include "LzmaBench.h" +#include "LzmaBenchCon.h" +#include "../../../Common/IntToString.h" + +#if defined(BENCH_MT) || defined(_WIN32) +#include "../../../Windows/System.h" +#endif + +#ifdef BREAK_HANDLER +#include "../../UI/Console/ConsoleClose.h" +#endif +#include "../../../Common/MyCom.h" + +struct CTotalBenchRes +{ + UInt64 NumIterations; + UInt64 Rating; + UInt64 Usage; + UInt64 RPU; + void Init() { NumIterations = 0; Rating = 0; Usage = 0; RPU = 0; } + void Normalize() + { + if (NumIterations == 0) + return; + Rating /= NumIterations; + Usage /= NumIterations; + RPU /= NumIterations; + NumIterations = 1; + } + void SetMid(const CTotalBenchRes &r1, const CTotalBenchRes &r2) + { + Rating = (r1.Rating + r2.Rating) / 2; + Usage = (r1.Usage + r2.Usage) / 2; + RPU = (r1.RPU + r2.RPU) / 2; + NumIterations = (r1.NumIterations + r2.NumIterations) / 2; + } +}; + +struct CBenchCallback: public IBenchCallback +{ + CTotalBenchRes EncodeRes; + CTotalBenchRes DecodeRes; + FILE *f; + void Init() { EncodeRes.Init(); DecodeRes.Init(); } + void Normalize() { EncodeRes.Normalize(); DecodeRes.Normalize(); } + UInt32 dictionarySize; + HRESULT SetEncodeResult(const CBenchInfo &info, bool final); + HRESULT SetDecodeResult(const CBenchInfo &info, bool final); +}; + +static void NormalizeVals(UInt64 &v1, UInt64 &v2) +{ + while (v1 > 1000000) + { + v1 >>= 1; + v2 >>= 1; + } +} + +static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq) +{ + UInt64 elTime = elapsedTime; + NormalizeVals(freq, elTime); + if (elTime == 0) + elTime = 1; + return value * freq / elTime; +} + +static void PrintNumber(FILE *f, UInt64 value, int size) +{ + char s[32]; + ConvertUInt64ToString(value, s); + fprintf(f, " "); + for (int len = (int)strlen(s); len < size; len++) + fprintf(f, " "); + fprintf(f, "%s", s); +} + +static void PrintRating(FILE *f, UInt64 rating) +{ + PrintNumber(f, rating / 1000000, 6); +} + +static void PrintResults(FILE *f, UInt64 usage, UInt64 rpu, UInt64 rating) +{ + PrintNumber(f, (usage + 5000) / 10000, 5); + PrintRating(f, rpu); + PrintRating(f, rating); +} + + +static void PrintResults(FILE *f, const CBenchInfo &info, UInt64 rating, CTotalBenchRes &res) +{ + UInt64 speed = MyMultDiv64(info.UnpackSize, info.GlobalTime, info.GlobalFreq); + PrintNumber(f, speed / 1024, 7); + UInt64 usage = GetUsage(info); + UInt64 rpu = GetRatingPerUsage(info, rating); + PrintResults(f, usage, rpu, rating); + res.NumIterations++; + res.RPU += rpu; + res.Rating += rating; + res.Usage += usage; +} + +static void PrintTotals(FILE *f, const CTotalBenchRes &res) +{ + fprintf(f, " "); + PrintResults(f, res.Usage, res.RPU, res.Rating); +} + + +HRESULT CBenchCallback::SetEncodeResult(const CBenchInfo &info, bool final) +{ + #ifdef BREAK_HANDLER + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + #endif + + if (final) + { + UInt64 rating = GetCompressRating(dictionarySize, info.GlobalTime, info.GlobalFreq, info.UnpackSize); + PrintResults(f, info, rating, EncodeRes); + } + return S_OK; +} + +static const char *kSep = " | "; + + +HRESULT CBenchCallback::SetDecodeResult(const CBenchInfo &info, bool final) +{ + #ifdef BREAK_HANDLER + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + #endif + if (final) + { + UInt64 rating = GetDecompressRating(info.GlobalTime, info.GlobalFreq, info.UnpackSize, info.PackSize, info.NumIterations); + fprintf(f, kSep); + CBenchInfo info2 = info; + info2.UnpackSize *= info2.NumIterations; + info2.PackSize *= info2.NumIterations; + info2.NumIterations = 1; + PrintResults(f, info2, rating, DecodeRes); + } + return S_OK; +} + +static void PrintRequirements(FILE *f, const char *sizeString, UInt64 size, const char *threadsString, UInt32 numThreads) +{ + fprintf(f, "\nRAM %s ", sizeString); + PrintNumber(f, (size >> 20), 5); + fprintf(f, " MB, # %s %3d", threadsString, (unsigned int)numThreads); +} + +HRESULT LzmaBenchCon( + #ifdef EXTERNAL_LZMA + CCodecs *codecs, + #endif + FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary) +{ + if (!CrcInternalTest()) + return S_FALSE; + #ifdef BENCH_MT + UInt64 ramSize = NWindows::NSystem::GetRamSize(); // + UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors(); + PrintRequirements(f, "size: ", ramSize, "CPU hardware threads:", numCPUs); + if (numThreads == (UInt32)-1) + numThreads = numCPUs; + if (numThreads > 1) + numThreads &= ~1; + if (dictionary == (UInt32)-1) + { + int dicSizeLog; + for (dicSizeLog = 25; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--) + if (GetBenchMemoryUsage(numThreads, ((UInt32)1 << dicSizeLog)) + (8 << 20) <= ramSize) + break; + dictionary = (1 << dicSizeLog); + } + #else + if (dictionary == (UInt32)-1) + dictionary = (1 << 22); + numThreads = 1; + #endif + + PrintRequirements(f, "usage:", GetBenchMemoryUsage(numThreads, dictionary), "Benchmark threads: ", numThreads); + + CBenchCallback callback; + callback.Init(); + callback.f = f; + + fprintf(f, "\n\nDict Compressing | Decompressing\n "); + int j; + for (j = 0; j < 2; j++) + { + fprintf(f, " Speed Usage R/U Rating"); + if (j == 0) + fprintf(f, kSep); + } + fprintf(f, "\n "); + for (j = 0; j < 2; j++) + { + fprintf(f, " KB/s %% MIPS MIPS"); + if (j == 0) + fprintf(f, kSep); + } + fprintf(f, "\n\n"); + for (UInt32 i = 0; i < numIterations; i++) + { + const int kStartDicLog = 22; + int pow = (dictionary < ((UInt32)1 << kStartDicLog)) ? kBenchMinDicLogSize : kStartDicLog; + while (((UInt32)1 << pow) > dictionary) + pow--; + for (; ((UInt32)1 << pow) <= dictionary; pow++) + { + fprintf(f, "%2d:", pow); + callback.dictionarySize = (UInt32)1 << pow; + HRESULT res = LzmaBench( + #ifdef EXTERNAL_LZMA + codecs, + #endif + numThreads, callback.dictionarySize, &callback); + fprintf(f, "\n"); + RINOK(res); + } + } + callback.Normalize(); + fprintf(f, "----------------------------------------------------------------\nAvr:"); + PrintTotals(f, callback.EncodeRes); + fprintf(f, " "); + PrintTotals(f, callback.DecodeRes); + fprintf(f, "\nTot:"); + CTotalBenchRes midRes; + midRes.SetMid(callback.EncodeRes, callback.DecodeRes); + PrintTotals(f, midRes); + fprintf(f, "\n"); + return S_OK; +} + +struct CTempValues +{ + UInt64 *Values; + CTempValues(UInt32 num) { Values = new UInt64[num]; } + ~CTempValues() { delete []Values; } +}; + +HRESULT CrcBenchCon(FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary) +{ + if (!CrcInternalTest()) + return S_FALSE; + + #ifdef BENCH_MT + UInt64 ramSize = NWindows::NSystem::GetRamSize(); + UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors(); + PrintRequirements(f, "size: ", ramSize, "CPU hardware threads:", numCPUs); + if (numThreads == (UInt32)-1) + numThreads = numCPUs; + #else + numThreads = 1; + #endif + if (dictionary == (UInt32)-1) + dictionary = (1 << 24); + + CTempValues speedTotals(numThreads); + fprintf(f, "\n\nSize"); + for (UInt32 ti = 0; ti < numThreads; ti++) + { + fprintf(f, " %5d", ti + 1); + speedTotals.Values[ti] = 0; + } + fprintf(f, "\n\n"); + + UInt64 numSteps = 0; + for (UInt32 i = 0; i < numIterations; i++) + { + for (int pow = 10; pow < 32; pow++) + { + UInt32 bufSize = (UInt32)1 << pow; + if (bufSize > dictionary) + break; + fprintf(f, "%2d: ", pow); + UInt64 speed; + for (UInt32 ti = 0; ti < numThreads; ti++) + { + #ifdef BREAK_HANDLER + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + #endif + RINOK(CrcBench(ti + 1, bufSize, speed)); + PrintNumber(f, (speed >> 20), 5); + speedTotals.Values[ti] += speed; + } + fprintf(f, "\n"); + numSteps++; + } + } + if (numSteps != 0) + { + fprintf(f, "\nAvg:"); + for (UInt32 ti = 0; ti < numThreads; ti++) + PrintNumber(f, ((speedTotals.Values[ti] / numSteps) >> 20), 5); + fprintf(f, "\n"); + } + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h new file mode 100644 index 0000000..ea8539d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h @@ -0,0 +1,20 @@ +// LzmaBenchCon.h + +#ifndef __LZMABENCHCON_H +#define __LZMABENCHCON_H + +#include +#include "../../../Common/Types.h" +#ifdef EXTERNAL_LZMA +#include "../../UI/Common/LoadCodecs.h" +#endif +HRESULT LzmaBenchCon( + #ifdef EXTERNAL_LZMA + CCodecs *codecs, + #endif + FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary); + +HRESULT CrcBenchCon(FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary); + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.cpp new file mode 100644 index 0000000..b86d1ea --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.cpp @@ -0,0 +1,226 @@ +// LzmaRam.cpp + +#include "StdAfx.h" +#include "../../../Common/Types.h" +#include "../LZMA/LZMADecoder.h" +#include "../LZMA/LZMAEncoder.h" +#include "LzmaRam.h" + +extern "C" +{ + #include "../../../../C/Compress/Branch/BranchX86.h" +} + +class CInStreamRam: + public ISequentialInStream, + public CMyUnknownImp +{ + const Byte *Data; + size_t Size; + size_t Pos; +public: + MY_UNKNOWN_IMP + void Init(const Byte *data, size_t size) + { + Data = data; + Size = size; + Pos = 0; + } + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (size > (Size - Pos)) + size = (UInt32)(Size - Pos); + for (UInt32 i = 0; i < size; i++) + ((Byte *)data)[i] = Data[Pos + i]; + Pos += size; + if(processedSize != NULL) + *processedSize = size; + return S_OK; +} + +class COutStreamRam: + public ISequentialOutStream, + public CMyUnknownImp +{ + size_t Size; +public: + Byte *Data; + size_t Pos; + bool Overflow; + void Init(Byte *data, size_t size) + { + Data = data; + Size = size; + Pos = 0; + Overflow = false; + } + void SetPos(size_t pos) + { + Overflow = false; + Pos = pos; + } + MY_UNKNOWN_IMP + HRESULT WriteByte(Byte b) + { + if (Pos >= Size) + { + Overflow = true; + return E_FAIL; + } + Data[Pos++] = b; + return S_OK; + } + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 i; + for (i = 0; i < size && Pos < Size; i++) + Data[Pos++] = ((const Byte *)data)[i]; + if(processedSize != NULL) + *processedSize = i; + if (i != size) + { + Overflow = true; + return E_FAIL; + } + return S_OK; +} + +#define SZ_RAM_E_FAIL (1) +#define SZ_RAM_E_OUTOFMEMORY (2) +#define SZE_OUT_OVERFLOW (3) + +int LzmaRamEncode( + const Byte *inBuffer, size_t inSize, + Byte *outBuffer, size_t outSize, size_t *outSizeProcessed, + UInt32 dictionarySize, ESzFilterMode filterMode) +{ + #ifndef _NO_EXCEPTIONS + try { + #endif + + *outSizeProcessed = 0; + const size_t kIdSize = 1; + const size_t kLzmaPropsSize = 5; + const size_t kMinDestSize = kIdSize + kLzmaPropsSize + 8; + if (outSize < kMinDestSize) + return SZE_OUT_OVERFLOW; + NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder; + CMyComPtr encoder = encoderSpec; + + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kDictionarySize, + NCoderPropID::kNumFastBytes, + }; + const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]); + PROPVARIANT properties[kNumProps]; + properties[0].vt = VT_UI4; + properties[1].vt = VT_UI4; + properties[2].vt = VT_UI4; + properties[0].ulVal = (UInt32)2; + properties[1].ulVal = (UInt32)dictionarySize; + properties[2].ulVal = (UInt32)64; + + if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK) + return 1; + + COutStreamRam *outStreamSpec = new COutStreamRam; + if (outStreamSpec == 0) + return SZ_RAM_E_OUTOFMEMORY; + CMyComPtr outStream = outStreamSpec; + CInStreamRam *inStreamSpec = new CInStreamRam; + if (inStreamSpec == 0) + return SZ_RAM_E_OUTOFMEMORY; + CMyComPtr inStream = inStreamSpec; + + outStreamSpec->Init(outBuffer, outSize); + if (outStreamSpec->WriteByte(0) != S_OK) + return SZE_OUT_OVERFLOW; + + if (encoderSpec->WriteCoderProperties(outStream) != S_OK) + return SZE_OUT_OVERFLOW; + if (outStreamSpec->Pos != kIdSize + kLzmaPropsSize) + return 1; + + int i; + for (i = 0; i < 8; i++) + { + UInt64 t = (UInt64)(inSize); + if (outStreamSpec->WriteByte((Byte)((t) >> (8 * i))) != S_OK) + return SZE_OUT_OVERFLOW; + } + + Byte *filteredStream = 0; + + bool useFilter = (filterMode != SZ_FILTER_NO); + if (useFilter) + { + if (inSize != 0) + { + filteredStream = (Byte *)MyAlloc(inSize); + if (filteredStream == 0) + return SZ_RAM_E_OUTOFMEMORY; + memmove(filteredStream, inBuffer, inSize); + } + UInt32 x86State; + x86_Convert_Init(x86State); + x86_Convert(filteredStream, (SizeT)inSize, 0, &x86State, 1); + } + + size_t minSize = 0; + int numPasses = (filterMode == SZ_FILTER_AUTO) ? 3 : 1; + bool bestIsFiltered = false; + int mainResult = 0; + size_t startPos = outStreamSpec->Pos; + for (i = 0; i < numPasses; i++) + { + if (numPasses > 1 && i == numPasses - 1 && !bestIsFiltered) + break; + outStreamSpec->SetPos(startPos); + bool curModeIsFiltered = false; + if (useFilter && i == 0) + curModeIsFiltered = true; + if (numPasses > 1 && i == numPasses - 1) + curModeIsFiltered = true; + + inStreamSpec->Init(curModeIsFiltered ? filteredStream : inBuffer, inSize); + + HRESULT lzmaResult = encoder->Code(inStream, outStream, 0, 0, 0); + + mainResult = 0; + if (lzmaResult == E_OUTOFMEMORY) + { + mainResult = SZ_RAM_E_OUTOFMEMORY; + break; + } + if (i == 0 || outStreamSpec->Pos <= minSize) + { + minSize = outStreamSpec->Pos; + bestIsFiltered = curModeIsFiltered; + } + if (outStreamSpec->Overflow) + mainResult = SZE_OUT_OVERFLOW; + else if (lzmaResult != S_OK) + { + mainResult = SZ_RAM_E_FAIL; + break; + } + } + *outSizeProcessed = outStreamSpec->Pos; + if (bestIsFiltered) + outBuffer[0] = 1; + if (useFilter) + MyFree(filteredStream); + return mainResult; + + #ifndef _NO_EXCEPTIONS + } catch(...) { return SZ_RAM_E_OUTOFMEMORY; } + #endif +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.h new file mode 100644 index 0000000..1244dc8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.h @@ -0,0 +1,46 @@ +// LzmaRam.h + +#ifndef __LzmaRam_h +#define __LzmaRam_h + +#include +#include "../../../Common/Types.h" + +/* +LzmaRamEncode: BCJ + LZMA RAM->RAM compressing. +It uses .lzma format, but it writes one additional byte to .lzma file: + 0: - no filter + 1: - x86(BCJ) filter. + +To provide best compression ratio dictionarySize mustbe >= inSize + +LzmaRamEncode allocates Data with MyAlloc/BigAlloc functions. +RAM Requirements: + RamSize = dictionarySize * 9.5 + 6MB + FilterBlockSize + FilterBlockSize = 0, if useFilter == false + FilterBlockSize = inSize, if useFilter == true + + Return code: + 0 - OK + 1 - Unspecified Error + 2 - Memory allocating error + 3 - Output buffer OVERFLOW + +If you use SZ_FILTER_AUTO mode, then encoder will use 2 or 3 passes: + 2 passes when FILTER_NO provides better compression. + 3 passes when FILTER_YES provides better compression. +*/ + +enum ESzFilterMode +{ + SZ_FILTER_NO, + SZ_FILTER_YES, + SZ_FILTER_AUTO +}; + +int LzmaRamEncode( + const Byte *inBuffer, size_t inSize, + Byte *outBuffer, size_t outSize, size_t *outSizeProcessed, + UInt32 dictionarySize, ESzFilterMode filterMode); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.c b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.c new file mode 100644 index 0000000..29f798b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.c @@ -0,0 +1,78 @@ +/* LzmaRamDecode.c */ + +#include "LzmaRamDecode.h" +#ifdef _SZ_ONE_DIRECTORY +#include "LzmaDecode.h" +#include "BranchX86.h" +#else +#include "../../../../C/Compress/Lzma/LzmaDecode.h" +#include "../../../../C/Compress/Branch/BranchX86.h" +#endif + +#define LZMA_PROPS_SIZE 14 +#define LZMA_SIZE_OFFSET 6 + +int LzmaRamGetUncompressedSize( + const unsigned char *inBuffer, + size_t inSize, + size_t *outSize) +{ + unsigned int i; + if (inSize < LZMA_PROPS_SIZE) + return 1; + *outSize = 0; + for(i = 0; i < sizeof(size_t); i++) + *outSize += ((size_t)inBuffer[LZMA_SIZE_OFFSET + i]) << (8 * i); + for(; i < 8; i++) + if (inBuffer[LZMA_SIZE_OFFSET + i] != 0) + return 1; + return 0; +} + +#define SZE_DATA_ERROR (1) +#define SZE_OUTOFMEMORY (2) + +int LzmaRamDecompress( + const unsigned char *inBuffer, + size_t inSize, + unsigned char *outBuffer, + size_t outSize, + size_t *outSizeProcessed, + void * (*allocFunc)(size_t size), + void (*freeFunc)(void *)) +{ + CLzmaDecoderState state; /* it's about 24 bytes structure, if int is 32-bit */ + int result; + SizeT outSizeProcessedLoc; + SizeT inProcessed; + int useFilter; + + if (inSize < LZMA_PROPS_SIZE) + return 1; + useFilter = inBuffer[0]; + + *outSizeProcessed = 0; + if (useFilter > 1) + return 1; + + if (LzmaDecodeProperties(&state.Properties, inBuffer + 1, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK) + return 1; + state.Probs = (CProb *)allocFunc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (state.Probs == 0) + return SZE_OUTOFMEMORY; + + result = LzmaDecode(&state, + inBuffer + LZMA_PROPS_SIZE, (SizeT)inSize - LZMA_PROPS_SIZE, &inProcessed, + outBuffer, (SizeT)outSize, &outSizeProcessedLoc); + freeFunc(state.Probs); + if (result != LZMA_RESULT_OK) + return 1; + *outSizeProcessed = (size_t)outSizeProcessedLoc; + if (useFilter == 1) + { + UInt32 x86State; + x86_Convert_Init(x86State); + x86_Convert(outBuffer, (SizeT)outSizeProcessedLoc, 0, &x86State, 0); + } + return 0; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.h new file mode 100644 index 0000000..7e641c5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.h @@ -0,0 +1,55 @@ +/* LzmaRamDecode.h */ + +#ifndef __LzmaRamDecode_h +#define __LzmaRamDecode_h + +#include + +/* +LzmaRamGetUncompressedSize: + In: + inBuffer - input data + inSize - input data size + Out: + outSize - uncompressed size + Return code: + 0 - OK + 1 - Error in headers +*/ + +int LzmaRamGetUncompressedSize( + const unsigned char *inBuffer, + size_t inSize, + size_t *outSize); + + +/* +LzmaRamDecompress: + In: + inBuffer - input data + inSize - input data size + outBuffer - output data + outSize - output size + allocFunc - alloc function (can be malloc) + freeFunc - free function (can be free) + Out: + outSizeProcessed - processed size + Return code: + 0 - OK + 1 - Error in headers / data stream + 2 - Memory allocating error + +Memory requirements depend from properties of LZMA stream. +With default lzma settings it's about 16 KB. +*/ + +int LzmaRamDecompress( + const unsigned char *inBuffer, + size_t inSize, + unsigned char *outBuffer, + size_t outSize, + size_t *outSizeProcessed, + void * (*allocFunc)(size_t size), + void (*freeFunc)(void *)); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.h new file mode 100644 index 0000000..e7fb698 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile new file mode 100644 index 0000000..16e7637 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile @@ -0,0 +1,136 @@ +PROG = lzma.exe +CFLAGS = $(CFLAGS) \ + -DCOMPRESS_MF_MT \ + -DBENCH_MT \ + +LIBS = $(LIBS) oleaut32.lib user32.lib + +!IFDEF CPU +LIBS = $(LIBS) bufferoverflowU.lib +CFLAGS = $(CFLAGS) -GS- -Zc:forScope -W4 -Wp64 -DUNICODE -D_UNICODE +!ENDIF + +!IFNDEF O +!IFDEF CPU +O=$(CPU) +!ELSE +O=O +!ENDIF +!ENDIF + +!IFDEF MY_STATIC_LINK +!IFNDEF MY_SINGLE_THREAD +CFLAGS = $(CFLAGS) -MT +!ENDIF +!ELSE +CFLAGS = $(CFLAGS) -MD +!ENDIF + +CFLAGS = $(CFLAGS) -nologo -EHsc -c -Fo$O/ +CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O2 = $(CFLAGS) -O2 + +LFLAGS = $(LFLAGS) -nologo -OPT:NOWIN98 + +PROGPATH = $O\$(PROG) + +COMPL_O1 = $(CPP) $(CFLAGS_O1) $** +COMPL_O2 = $(CPP) $(CFLAGS_O2) $** +COMPL = $(CPP) $(CFLAGS_O1) $** + + +LZMA_OBJS = \ + $O\LzmaAlone.obj \ + $O\LzmaBench.obj \ + $O\LzmaBenchCon.obj \ + $O\LzmaRam.obj \ + +LZMA_OPT_OBJS = \ + $O\LZMADecoder.obj \ + $O\LZMAEncoder.obj \ + +COMMON_OBJS = \ + $O\CommandLineParser.obj \ + $O\CRC.obj \ + $O\IntToString.obj \ + $O\MyString.obj \ + $O\StringConvert.obj \ + $O\StringToInt.obj \ + $O\MyVector.obj + +WIN_OBJS = \ + $O\System.obj + +7ZIP_COMMON_OBJS = \ + $O\InBuffer.obj \ + $O\OutBuffer.obj \ + $O\StreamUtils.obj \ + +LZ_OBJS = \ + $O\LZOutWindow.obj \ + +C_OBJS = \ + $O\Alloc.obj \ + $O\7zCrc.obj \ + $O\Threads.obj \ + +C_LZ_OBJS = \ + $O\MatchFinder.obj \ + $O\MatchFinderMt.obj \ + +OBJS = \ + $(LZMA_OBJS) \ + $(LZMA_OPT_OBJS) \ + $(COMMON_OBJS) \ + $(WIN_OBJS) \ + $(7ZIP_COMMON_OBJS) \ + $(LZ_OBJS) \ + $(C_OBJS) \ + $(C_LZ_OBJS) \ + $O\LzmaRamDecode.obj \ + $O\LzmaDecode.obj \ + $O\FileStreams.obj \ + $O\FileIO.obj \ + $O\RangeCoderBit.obj \ + $O\BranchX86.obj \ + +all: $(PROGPATH) + +clean: + -del /Q $(PROGPATH) $O\*.exe $O\*.dll $O\*.obj $O\*.lib $O\*.exp $O\*.res $O\*.pch + +$O: + if not exist "$O" mkdir "$O" + +$(PROGPATH): $O $(OBJS) + link $(LFLAGS) -out:$(PROGPATH) $(OBJS) $(LIBS) + + +$(LZMA_OBJS): $(*B).cpp + $(COMPL) +$(LZMA_OPT_OBJS): ../LZMA/$(*B).cpp + $(COMPL_O2) +$(COMMON_OBJS): ../../../Common/$(*B).cpp + $(COMPL) +$(WIN_OBJS): ../../../Windows/$(*B).cpp + $(COMPL) +$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp + $(COMPL) +$(LZ_OBJS): ../LZ/$(*B).cpp + $(COMPL) +$O\RangeCoderBit.obj: ../RangeCoder/$(*B).cpp + $(COMPL) +$O\LzmaRamDecode.obj: LzmaRamDecode.c + $(COMPL_O1) +$O\LzmaDecode.obj: ../../../../C/Compress/Lzma/LzmaDecode.c + $(COMPL_O2) +$O\BranchX86.obj: ../../../../C/Compress/Branch/BranchX86.c + $(COMPL_O2) +$O\FileStreams.obj: ../../Common/FileStreams.cpp + $(COMPL) +$O\FileIO.obj: ../../../Windows/FileIO.cpp + $(COMPL) +$(C_OBJS): ../../../../C/$(*B).c + $(COMPL_O2) +$(C_LZ_OBJS): ../../../../C/Compress/Lz/$(*B).c + $(COMPL_O2) diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile.gcc b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile.gcc new file mode 100644 index 0000000..4fed05e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile.gcc @@ -0,0 +1,139 @@ +PROG = lzma +CXX = g++ -O2 -Wall +CXX_C = gcc -O2 -Wall +LIB = -lm +RM = rm -f +CFLAGS = -c + +ifdef SystemDrive +IS_MINGW = 1 +endif + +ifdef IS_MINGW +FILE_IO =FileIO +FILE_IO_2 =Windows/$(FILE_IO) +LIB2 = -luuid +else +FILE_IO =C_FileIO +FILE_IO_2 =Common/$(FILE_IO) +endif + +OBJS = \ + LzmaAlone.o \ + LzmaBench.o \ + LzmaBenchCon.o \ + LzmaRam.o \ + LZMADecoder.o \ + LZMAEncoder.o \ + LZOutWindow.o \ + RangeCoderBit.o \ + InBuffer.o \ + OutBuffer.o \ + FileStreams.o \ + StreamUtils.o \ + $(FILE_IO).o \ + CommandLineParser.o \ + CRC.o \ + IntToString.o \ + MyString.o \ + StringConvert.o \ + StringToInt.o \ + MyVector.o \ + 7zCrc.o \ + Alloc.o \ + BranchX86.o \ + MatchFinder.o \ + LzmaDecode.o \ + LzmaRamDecode.o \ + + +all: $(PROG) + +$(PROG): $(OBJS) + $(CXX) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIB) $(LIB2) + +LzmaAlone.o: LzmaAlone.cpp + $(CXX) $(CFLAGS) LzmaAlone.cpp + +LzmaBench.o: LzmaBench.cpp + $(CXX) $(CFLAGS) LzmaBench.cpp + +LzmaBenchCon.o: LzmaBenchCon.cpp + $(CXX) $(CFLAGS) LzmaBenchCon.cpp + +LzmaRam.o: LzmaRam.cpp + $(CXX) $(CFLAGS) LzmaRam.cpp + +LZMADecoder.o: ../LZMA/LZMADecoder.cpp + $(CXX) $(CFLAGS) ../LZMA/LZMADecoder.cpp + +LZMAEncoder.o: ../LZMA/LZMAEncoder.cpp + $(CXX) $(CFLAGS) ../LZMA/LZMAEncoder.cpp + +LZOutWindow.o: ../LZ/LZOutWindow.cpp + $(CXX) $(CFLAGS) ../LZ/LZOutWindow.cpp + +RangeCoderBit.o: ../RangeCoder/RangeCoderBit.cpp + $(CXX) $(CFLAGS) ../RangeCoder/RangeCoderBit.cpp + +InBuffer.o: ../../Common/InBuffer.cpp + $(CXX) $(CFLAGS) ../../Common/InBuffer.cpp + +OutBuffer.o: ../../Common/OutBuffer.cpp + $(CXX) $(CFLAGS) ../../Common/OutBuffer.cpp + +FileStreams.o: ../../Common/FileStreams.cpp + $(CXX) $(CFLAGS) ../../Common/FileStreams.cpp + +StreamUtils.o: ../../Common/StreamUtils.cpp + $(CXX) $(CFLAGS) ../../Common/StreamUtils.cpp + +$(FILE_IO).o: ../../../$(FILE_IO_2).cpp + $(CXX) $(CFLAGS) ../../../$(FILE_IO_2).cpp + + +CommandLineParser.o: ../../../Common/CommandLineParser.cpp + $(CXX) $(CFLAGS) ../../../Common/CommandLineParser.cpp + +CRC.o: ../../../Common/CRC.cpp + $(CXX) $(CFLAGS) ../../../Common/CRC.cpp + +MyWindows.o: ../../../Common/MyWindows.cpp + $(CXX) $(CFLAGS) ../../../Common/MyWindows.cpp + +IntToString.o: ../../../Common/IntToString.cpp + $(CXX) $(CFLAGS) ../../../Common/IntToString.cpp + +MyString.o: ../../../Common/MyString.cpp + $(CXX) $(CFLAGS) ../../../Common/MyString.cpp + +StringConvert.o: ../../../Common/StringConvert.cpp + $(CXX) $(CFLAGS) ../../../Common/StringConvert.cpp + +StringToInt.o: ../../../Common/StringToInt.cpp + $(CXX) $(CFLAGS) ../../../Common/StringToInt.cpp + +MyVector.o: ../../../Common/MyVector.cpp + $(CXX) $(CFLAGS) ../../../Common/MyVector.cpp + +7zCrc.o: ../../../../C/7zCrc.c + $(CXX_C) $(CFLAGS) ../../../../C/7zCrc.c + +Alloc.o: ../../../../C/Alloc.c + $(CXX_C) $(CFLAGS) ../../../../C/Alloc.c + +BranchX86.o: ../../../../C/Compress/Branch/BranchX86.c + $(CXX_C) $(CFLAGS) ../../../../C/Compress/Branch/BranchX86.c + +MatchFinder.o: ../../../../C/Compress/Lz/MatchFinder.c + $(CXX_C) $(CFLAGS) ../../../../C/Compress/Lz/MatchFinder.c + +LzmaDecode.o: ../../../../C/Compress/Lzma/LzmaDecode.c + $(CXX_C) $(CFLAGS) ../../../../C/Compress/Lzma/LzmaDecode.c + +LzmaRamDecode.o: LzmaRamDecode.c + $(CXX_C) $(CFLAGS) LzmaRamDecode.c + +clean: + -$(RM) $(PROG) $(OBJS) + diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoder.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoder.h new file mode 100644 index 0000000..bbb2ba8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoder.h @@ -0,0 +1,205 @@ +// Compress/RangeCoder/RangeCoder.h + +#ifndef __COMPRESS_RANGECODER_H +#define __COMPRESS_RANGECODER_H + +#include "../../Common/InBuffer.h" +#include "../../Common/OutBuffer.h" + +namespace NCompress { +namespace NRangeCoder { + +const int kNumTopBits = 24; +const UInt32 kTopValue = (1 << kNumTopBits); + +class CEncoder +{ + UInt32 _cacheSize; + Byte _cache; +public: + UInt64 Low; + UInt32 Range; + COutBuffer Stream; + bool Create(UInt32 bufferSize) { return Stream.Create(bufferSize); } + + void SetStream(ISequentialOutStream *stream) { Stream.SetStream(stream); } + void Init() + { + Stream.Init(); + Low = 0; + Range = 0xFFFFFFFF; + _cacheSize = 1; + _cache = 0; + } + + void FlushData() + { + // Low += 1; + for(int i = 0; i < 5; i++) + ShiftLow(); + } + + HRESULT FlushStream() { return Stream.Flush(); } + + void ReleaseStream() { Stream.ReleaseStream(); } + + void Encode(UInt32 start, UInt32 size, UInt32 total) + { + Low += start * (Range /= total); + Range *= size; + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + void ShiftLow() + { + if ((UInt32)Low < (UInt32)0xFF000000 || (int)(Low >> 32) != 0) + { + Byte temp = _cache; + do + { + Stream.WriteByte((Byte)(temp + (Byte)(Low >> 32))); + temp = 0xFF; + } + while(--_cacheSize != 0); + _cache = (Byte)((UInt32)Low >> 24); + } + _cacheSize++; + Low = (UInt32)Low << 8; + } + + void EncodeDirectBits(UInt32 value, int numTotalBits) + { + for (int i = numTotalBits - 1; i >= 0; i--) + { + Range >>= 1; + if (((value >> i) & 1) == 1) + Low += Range; + if (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + } + + void EncodeBit(UInt32 size0, UInt32 numTotalBits, UInt32 symbol) + { + UInt32 newBound = (Range >> numTotalBits) * size0; + if (symbol == 0) + Range = newBound; + else + { + Low += newBound; + Range -= newBound; + } + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + UInt64 GetProcessedSize() { return Stream.GetProcessedSize() + _cacheSize + 4; } +}; + +class CDecoder +{ +public: + CInBuffer Stream; + UInt32 Range; + UInt32 Code; + bool Create(UInt32 bufferSize) { return Stream.Create(bufferSize); } + + void Normalize() + { + while (Range < kTopValue) + { + Code = (Code << 8) | Stream.ReadByte(); + Range <<= 8; + } + } + + void SetStream(ISequentialInStream *stream) { Stream.SetStream(stream); } + void Init() + { + Stream.Init(); + Code = 0; + Range = 0xFFFFFFFF; + for(int i = 0; i < 5; i++) + Code = (Code << 8) | Stream.ReadByte(); + } + + void ReleaseStream() { Stream.ReleaseStream(); } + + UInt32 GetThreshold(UInt32 total) + { + return (Code) / ( Range /= total); + } + + void Decode(UInt32 start, UInt32 size) + { + Code -= start * Range; + Range *= size; + Normalize(); + } + + UInt32 DecodeDirectBits(int numTotalBits) + { + UInt32 range = Range; + UInt32 code = Code; + UInt32 result = 0; + for (int i = numTotalBits; i != 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + UInt32 t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < kTopValue) + { + code = (code << 8) | Stream.ReadByte(); + range <<= 8; + } + } + Range = range; + Code = code; + return result; + } + + UInt32 DecodeBit(UInt32 size0, UInt32 numTotalBits) + { + UInt32 newBound = (Range >> numTotalBits) * size0; + UInt32 symbol; + if (Code < newBound) + { + symbol = 0; + Range = newBound; + } + else + { + symbol = 1; + Code -= newBound; + Range -= newBound; + } + Normalize(); + return symbol; + } + + UInt64 GetProcessedSize() {return Stream.GetProcessedSize(); } +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.cpp b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.cpp new file mode 100644 index 0000000..8e4c4d3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.cpp @@ -0,0 +1,80 @@ +// Compress/RangeCoder/RangeCoderBit.cpp + +#include "StdAfx.h" + +#include "RangeCoderBit.h" + +namespace NCompress { +namespace NRangeCoder { + +UInt32 CPriceTables::ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; +static CPriceTables g_PriceTables; + +CPriceTables::CPriceTables() { Init(); } + +void CPriceTables::Init() +{ + const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + for(int i = kNumBits - 1; i >= 0; i--) + { + UInt32 start = 1 << (kNumBits - i - 1); + UInt32 end = 1 << (kNumBits - i); + for (UInt32 j = start; j < end; j++) + ProbPrices[j] = (i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1)); + } + + /* + // simplest: bad solution + for(UInt32 i = 1; i < (kBitModelTotal >> kNumMoveReducingBits) - 1; i++) + ProbPrices[i] = kBitPrice; + */ + + /* + const double kDummyMultMid = (1.0 / kBitPrice) / 2; + const double kDummyMultMid = 0; + // float solution + double ln2 = log(double(2)); + double lnAll = log(double(kBitModelTotal >> kNumMoveReducingBits)); + for(UInt32 i = 1; i < (kBitModelTotal >> kNumMoveReducingBits) - 1; i++) + ProbPrices[i] = UInt32((fabs(lnAll - log(double(i))) / ln2 + kDummyMultMid) * kBitPrice); + */ + + /* + // experimental, slow, solution: + for(UInt32 i = 1; i < (kBitModelTotal >> kNumMoveReducingBits) - 1; i++) + { + const int kCyclesBits = 5; + const UInt32 kCycles = (1 << kCyclesBits); + + UInt32 range = UInt32(-1); + UInt32 bitCount = 0; + for (UInt32 j = 0; j < kCycles; j++) + { + range >>= (kNumBitModelTotalBits - kNumMoveReducingBits); + range *= i; + while(range < (1 << 31)) + { + range <<= 1; + bitCount++; + } + } + bitCount <<= kNumBitPriceShiftBits; + range -= (1 << 31); + for (int k = kNumBitPriceShiftBits - 1; k >= 0; k--) + { + range <<= 1; + if (range > (1 << 31)) + { + bitCount += (1 << k); + range -= (1 << 31); + } + } + ProbPrices[i] = (bitCount + // + (1 << (kCyclesBits - 1)) + ) >> kCyclesBits; + } + */ +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.h new file mode 100644 index 0000000..624f887 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.h @@ -0,0 +1,120 @@ +// Compress/RangeCoder/RangeCoderBit.h + +#ifndef __COMPRESS_RANGECODER_BIT_H +#define __COMPRESS_RANGECODER_BIT_H + +#include "RangeCoder.h" + +namespace NCompress { +namespace NRangeCoder { + +const int kNumBitModelTotalBits = 11; +const UInt32 kBitModelTotal = (1 << kNumBitModelTotalBits); + +const int kNumMoveReducingBits = 2; + +const int kNumBitPriceShiftBits = 6; +const UInt32 kBitPrice = 1 << kNumBitPriceShiftBits; + +class CPriceTables +{ +public: + static UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + static void Init(); + CPriceTables(); +}; + +template +class CBitModel +{ +public: + UInt32 Prob; + void UpdateModel(UInt32 symbol) + { + /* + Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits; + Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits); + */ + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> numMoveBits; + else + Prob -= (Prob) >> numMoveBits; + } +public: + void Init() { Prob = kBitModelTotal / 2; } +}; + +template +class CBitEncoder: public CBitModel +{ +public: + void Encode(CEncoder *encoder, UInt32 symbol) + { + /* + encoder->EncodeBit(this->Prob, kNumBitModelTotalBits, symbol); + this->UpdateModel(symbol); + */ + UInt32 newBound = (encoder->Range >> kNumBitModelTotalBits) * this->Prob; + if (symbol == 0) + { + encoder->Range = newBound; + this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; + } + else + { + encoder->Low += newBound; + encoder->Range -= newBound; + this->Prob -= (this->Prob) >> numMoveBits; + } + if (encoder->Range < kTopValue) + { + encoder->Range <<= 8; + encoder->ShiftLow(); + } + } + UInt32 GetPrice(UInt32 symbol) const + { + return CPriceTables::ProbPrices[ + (((this->Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; + } + UInt32 GetPrice0() const { return CPriceTables::ProbPrices[this->Prob >> kNumMoveReducingBits]; } + UInt32 GetPrice1() const { return CPriceTables::ProbPrices[(kBitModelTotal - this->Prob) >> kNumMoveReducingBits]; } +}; + + +template +class CBitDecoder: public CBitModel +{ +public: + UInt32 Decode(CDecoder *decoder) + { + UInt32 newBound = (decoder->Range >> kNumBitModelTotalBits) * this->Prob; + if (decoder->Code < newBound) + { + decoder->Range = newBound; + this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; + if (decoder->Range < kTopValue) + { + decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); + decoder->Range <<= 8; + } + return 0; + } + else + { + decoder->Range -= newBound; + decoder->Code -= newBound; + this->Prob -= (this->Prob) >> numMoveBits; + if (decoder->Range < kTopValue) + { + decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); + decoder->Range <<= 8; + } + return 1; + } + } +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBitTree.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBitTree.h new file mode 100644 index 0000000..4f0c78b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBitTree.h @@ -0,0 +1,161 @@ +// Compress/RangeCoder/RangeCoderBitTree.h + +#ifndef __COMPRESS_RANGECODER_BIT_TREE_H +#define __COMPRESS_RANGECODER_BIT_TREE_H + +#include "RangeCoderBit.h" +#include "RangeCoderOpt.h" + +namespace NCompress { +namespace NRangeCoder { + +template +class CBitTreeEncoder +{ + CBitEncoder Models[1 << NumBitLevels]; +public: + void Init() + { + for(UInt32 i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + void Encode(CEncoder *rangeEncoder, UInt32 symbol) + { + UInt32 modelIndex = 1; + for (int bitIndex = NumBitLevels; bitIndex != 0 ;) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + Models[modelIndex].Encode(rangeEncoder, bit); + modelIndex = (modelIndex << 1) | bit; + } + }; + void ReverseEncode(CEncoder *rangeEncoder, UInt32 symbol) + { + UInt32 modelIndex = 1; + for (int i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[modelIndex].Encode(rangeEncoder, bit); + modelIndex = (modelIndex << 1) | bit; + symbol >>= 1; + } + } + UInt32 GetPrice(UInt32 symbol) const + { + symbol |= (1 << NumBitLevels); + UInt32 price = 0; + while (symbol != 1) + { + price += Models[symbol >> 1].GetPrice(symbol & 1); + symbol >>= 1; + } + return price; + } + UInt32 ReverseGetPrice(UInt32 symbol) const + { + UInt32 price = 0; + UInt32 modelIndex = 1; + for (int i = NumBitLevels; i != 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[modelIndex].GetPrice(bit); + modelIndex = (modelIndex << 1) | bit; + } + return price; + } +}; + +template +class CBitTreeDecoder +{ + CBitDecoder Models[1 << NumBitLevels]; +public: + void Init() + { + for(UInt32 i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + UInt32 Decode(CDecoder *rangeDecoder) + { + UInt32 modelIndex = 1; + RC_INIT_VAR + for(int bitIndex = NumBitLevels; bitIndex != 0; bitIndex--) + { + // modelIndex = (modelIndex << 1) + Models[modelIndex].Decode(rangeDecoder); + RC_GETBIT(numMoveBits, Models[modelIndex].Prob, modelIndex) + } + RC_FLUSH_VAR + return modelIndex - (1 << NumBitLevels); + }; + UInt32 ReverseDecode(CDecoder *rangeDecoder) + { + UInt32 modelIndex = 1; + UInt32 symbol = 0; + RC_INIT_VAR + for(int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + // UInt32 bit = Models[modelIndex].Decode(rangeDecoder); + // modelIndex <<= 1; + // modelIndex += bit; + // symbol |= (bit << bitIndex); + RC_GETBIT2(numMoveBits, Models[modelIndex].Prob, modelIndex, ; , symbol |= (1 << bitIndex)) + } + RC_FLUSH_VAR + return symbol; + } +}; + +template +void ReverseBitTreeEncode(CBitEncoder *Models, + CEncoder *rangeEncoder, int NumBitLevels, UInt32 symbol) +{ + UInt32 modelIndex = 1; + for (int i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[modelIndex].Encode(rangeEncoder, bit); + modelIndex = (modelIndex << 1) | bit; + symbol >>= 1; + } +} + +template +UInt32 ReverseBitTreeGetPrice(CBitEncoder *Models, + UInt32 NumBitLevels, UInt32 symbol) +{ + UInt32 price = 0; + UInt32 modelIndex = 1; + for (int i = NumBitLevels; i != 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[modelIndex].GetPrice(bit); + modelIndex = (modelIndex << 1) | bit; + } + return price; +} + +template +UInt32 ReverseBitTreeDecode(CBitDecoder *Models, + CDecoder *rangeDecoder, int NumBitLevels) +{ + UInt32 modelIndex = 1; + UInt32 symbol = 0; + RC_INIT_VAR + for(int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + // UInt32 bit = Models[modelIndex].Decode(rangeDecoder); + // modelIndex <<= 1; + // modelIndex += bit; + // symbol |= (bit << bitIndex); + RC_GETBIT2(numMoveBits, Models[modelIndex].Prob, modelIndex, ; , symbol |= (1 << bitIndex)) + } + RC_FLUSH_VAR + return symbol; +} + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderOpt.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderOpt.h new file mode 100644 index 0000000..668b9a5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderOpt.h @@ -0,0 +1,31 @@ +// Compress/RangeCoder/RangeCoderOpt.h + +#ifndef __COMPRESS_RANGECODER_OPT_H +#define __COMPRESS_RANGECODER_OPT_H + +#define RC_INIT_VAR \ + UInt32 range = rangeDecoder->Range; \ + UInt32 code = rangeDecoder->Code; + +#define RC_FLUSH_VAR \ + rangeDecoder->Range = range; \ + rangeDecoder->Code = code; + +#define RC_NORMALIZE \ + if (range < NCompress::NRangeCoder::kTopValue) \ + { code = (code << 8) | rangeDecoder->Stream.ReadByte(); range <<= 8; } + +#define RC_GETBIT2(numMoveBits, prob, mi, A0, A1) \ + { UInt32 bound = (range >> NCompress::NRangeCoder::kNumBitModelTotalBits) * prob; \ + if (code < bound) \ + { A0; range = bound; \ + prob += (NCompress::NRangeCoder::kBitModelTotal - prob) >> numMoveBits; \ + mi <<= 1; } \ + else \ + { A1; range -= bound; code -= bound; prob -= (prob) >> numMoveBits; \ + mi = (mi + mi) + 1; }} \ + RC_NORMALIZE + +#define RC_GETBIT(numMoveBits, prob, mi) RC_GETBIT2(numMoveBits, prob, mi, ; , ;) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/StdAfx.h new file mode 100644 index 0000000..b637fd4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/Compress/RangeCoder/StdAfx.h @@ -0,0 +1,6 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/ICoder.h b/3rdparty/physfs/lzma/CPP/7zip/ICoder.h new file mode 100644 index 0000000..a497653 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/ICoder.h @@ -0,0 +1,185 @@ +// ICoder.h + +#ifndef __ICODER_H +#define __ICODER_H + +#include "IStream.h" + +#define CODER_INTERFACE(i, x) DECL_INTERFACE(i, 4, x) + +CODER_INTERFACE(ICompressProgressInfo, 0x04) +{ + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize) PURE; +}; + +CODER_INTERFACE(ICompressCoder, 0x05) +{ + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, + const UInt64 *inSize, + const UInt64 *outSize, + ICompressProgressInfo *progress) PURE; +}; + +CODER_INTERFACE(ICompressCoder2, 0x18) +{ + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress) PURE; +}; + +namespace NCoderPropID +{ + enum EEnum + { + kDictionarySize = 0x400, + kUsedMemorySize, + kOrder, + kPosStateBits = 0x440, + kLitContextBits, + kLitPosBits, + kNumFastBytes = 0x450, + kMatchFinder, + kMatchFinderCycles, + kNumPasses = 0x460, + kAlgorithm = 0x470, + kMultiThread = 0x480, + kNumThreads, + kEndMarker = 0x490 + }; +} + +CODER_INTERFACE(ICompressSetCoderProperties, 0x20) +{ + STDMETHOD(SetCoderProperties)(const PROPID *propIDs, + const PROPVARIANT *properties, UInt32 numProperties) PURE; +}; + +/* +CODER_INTERFACE(ICompressSetCoderProperties, 0x21) +{ + STDMETHOD(SetDecoderProperties)(ISequentialInStream *inStream) PURE; +}; +*/ + +CODER_INTERFACE(ICompressSetDecoderProperties2, 0x22) +{ + STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size) PURE; +}; + +CODER_INTERFACE(ICompressWriteCoderProperties, 0x23) +{ + STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStreams) PURE; +}; + +CODER_INTERFACE(ICompressGetInStreamProcessedSize, 0x24) +{ + STDMETHOD(GetInStreamProcessedSize)(UInt64 *value) PURE; +}; + +CODER_INTERFACE(ICompressSetCoderMt, 0x25) +{ + STDMETHOD(SetNumberOfThreads)(UInt32 numThreads) PURE; +}; + +CODER_INTERFACE(ICompressGetSubStreamSize, 0x30) +{ + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value) PURE; +}; + +CODER_INTERFACE(ICompressSetInStream, 0x31) +{ + STDMETHOD(SetInStream)(ISequentialInStream *inStream) PURE; + STDMETHOD(ReleaseInStream)() PURE; +}; + +CODER_INTERFACE(ICompressSetOutStream, 0x32) +{ + STDMETHOD(SetOutStream)(ISequentialOutStream *outStream) PURE; + STDMETHOD(ReleaseOutStream)() PURE; +}; + +CODER_INTERFACE(ICompressSetInStreamSize, 0x33) +{ + STDMETHOD(SetInStreamSize)(const UInt64 *inSize) PURE; +}; + +CODER_INTERFACE(ICompressSetOutStreamSize, 0x34) +{ + STDMETHOD(SetOutStreamSize)(const UInt64 *outSize) PURE; +}; + +CODER_INTERFACE(ICompressFilter, 0x40) +{ + STDMETHOD(Init)() PURE; + STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size) PURE; + // Filter return outSize (UInt32) + // if (outSize <= size): Filter have converted outSize bytes + // if (outSize > size): Filter have not converted anything. + // and it needs at least outSize bytes to convert one block + // (it's for crypto block algorithms). +}; + +CODER_INTERFACE(ICompressCodecsInfo, 0x60) +{ + STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods) PURE; + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; + STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder) PURE; + STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder) PURE; +}; +CODER_INTERFACE(ISetCompressCodecsInfo, 0x61) +{ + STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo) PURE; +}; + +CODER_INTERFACE(ICryptoProperties, 0x80) +{ + STDMETHOD(SetKey)(const Byte *data, UInt32 size) PURE; + STDMETHOD(SetInitVector)(const Byte *data, UInt32 size) PURE; +}; + +/* +CODER_INTERFACE(ICryptoResetSalt, 0x88) +{ + STDMETHOD(ResetSalt)() PURE; +}; +*/ + +CODER_INTERFACE(ICryptoResetInitVector, 0x8C) +{ + STDMETHOD(ResetInitVector)() PURE; +}; + +CODER_INTERFACE(ICryptoSetPassword, 0x90) +{ + STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size) PURE; +}; + +CODER_INTERFACE(ICryptoSetCRC, 0xA0) +{ + STDMETHOD(CryptoSetCRC)(UInt32 crc) PURE; +}; + +////////////////////// +// It's for DLL file +namespace NMethodPropID +{ + enum EEnum + { + kID, + kName, + kDecoder, + kEncoder, + kInStreams, + kOutStreams, + kDescription, + kDecoderIsAssigned, + kEncoderIsAssigned + }; +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/IDecl.h b/3rdparty/physfs/lzma/CPP/7zip/IDecl.h new file mode 100644 index 0000000..8316eb3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/IDecl.h @@ -0,0 +1,15 @@ +// IDecl.h + +#ifndef __IDECL_H +#define __IDECL_H + +#include "../Common/MyUnknown.h" + +#define DECL_INTERFACE_SUB(i, base, groupId, subId) \ +DEFINE_GUID(IID_ ## i, \ +0x23170F69, 0x40C1, 0x278A, 0, 0, 0, (groupId), 0, (subId), 0, 0); \ +struct i: public base + +#define DECL_INTERFACE(i, groupId, subId) DECL_INTERFACE_SUB(i, IUnknown, groupId, subId) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/IPassword.h b/3rdparty/physfs/lzma/CPP/7zip/IPassword.h new file mode 100644 index 0000000..3ca7b09 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/IPassword.h @@ -0,0 +1,24 @@ +// IPassword.h + +#ifndef __IPASSWORD_H +#define __IPASSWORD_H + +#include "../Common/MyUnknown.h" +#include "../Common/Types.h" + +#include "IDecl.h" + +#define PASSWORD_INTERFACE(i, x) DECL_INTERFACE(i, 5, x) + +PASSWORD_INTERFACE(ICryptoGetTextPassword, 0x10) +{ + STDMETHOD(CryptoGetTextPassword)(BSTR *password) PURE; +}; + +PASSWORD_INTERFACE(ICryptoGetTextPassword2, 0x11) +{ + STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password) PURE; +}; + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/IProgress.h b/3rdparty/physfs/lzma/CPP/7zip/IProgress.h new file mode 100644 index 0000000..f5f54b2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/IProgress.h @@ -0,0 +1,30 @@ +// Interface/IProgress.h + +#ifndef __IPROGRESS_H +#define __IPROGRESS_H + +#include "../Common/MyUnknown.h" +#include "../Common/Types.h" + +#include "IDecl.h" + +DECL_INTERFACE(IProgress, 0, 5) +{ + STDMETHOD(SetTotal)(UInt64 total) PURE; + STDMETHOD(SetCompleted)(const UInt64 *completeValue) PURE; +}; + +/* +// {23170F69-40C1-278A-0000-000000050002} +DEFINE_GUID(IID_IProgress2, +0x23170F69, 0x40C1, 0x278A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02); +MIDL_INTERFACE("23170F69-40C1-278A-0000-000000050002") +IProgress2: public IUnknown +{ +public: + STDMETHOD(SetTotal)(const UInt64 *total) PURE; + STDMETHOD(SetCompleted)(const UInt64 *completeValue) PURE; +}; +*/ + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/IStream.h b/3rdparty/physfs/lzma/CPP/7zip/IStream.h new file mode 100644 index 0000000..a19d04f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/IStream.h @@ -0,0 +1,58 @@ +// IStream.h + +#ifndef __ISTREAM_H +#define __ISTREAM_H + +#include "../Common/MyUnknown.h" +#include "../Common/Types.h" + +#include "IDecl.h" + +#define STREAM_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 3, x) +#define STREAM_INTERFACE(i, x) STREAM_INTERFACE_SUB(i, IUnknown, x) + +STREAM_INTERFACE(ISequentialInStream, 0x01) +{ + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) PURE; + /* + Out: if size != 0, return_value = S_OK and (*processedSize == 0), + then there are no more bytes in stream. + if (size > 0) && there are bytes in stream, + this function must read at least 1 byte. + This function is allowed to read less than number of remaining bytes in stream. + You must call Read function in loop, if you need exact amount of data + */ +}; + +STREAM_INTERFACE(ISequentialOutStream, 0x02) +{ + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) PURE; + /* + if (size > 0) this function must write at least 1 byte. + This function is allowed to write less than "size". + You must call Write function in loop, if you need to write exact amount of data + */ +}; + +STREAM_INTERFACE_SUB(IInStream, ISequentialInStream, 0x03) +{ + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; +}; + +STREAM_INTERFACE_SUB(IOutStream, ISequentialOutStream, 0x04) +{ + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; + STDMETHOD(SetSize)(Int64 newSize) PURE; +}; + +STREAM_INTERFACE(IStreamGetSize, 0x06) +{ + STDMETHOD(GetSize)(UInt64 *size) PURE; +}; + +STREAM_INTERFACE(IOutStreamFlush, 0x07) +{ + STDMETHOD(Flush)() PURE; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/MyVersion.h b/3rdparty/physfs/lzma/CPP/7zip/MyVersion.h new file mode 100644 index 0000000..af5c7d3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/MyVersion.h @@ -0,0 +1,8 @@ +#define MY_VER_MAJOR 4 +#define MY_VER_MINOR 57 +#define MY_VER_BUILD 0 +#define MY_VERSION "4.57" +#define MY_7ZIP_VERSION "7-Zip 4.57" +#define MY_DATE "2007-12-06" +#define MY_COPYRIGHT "Copyright (c) 1999-2007 Igor Pavlov" +#define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " " MY_DATE diff --git a/3rdparty/physfs/lzma/CPP/7zip/MyVersionInfo.rc b/3rdparty/physfs/lzma/CPP/7zip/MyVersionInfo.rc new file mode 100644 index 0000000..c3712b1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/MyVersionInfo.rc @@ -0,0 +1,45 @@ +#include +#include "MyVersion.h" + +#define MY_VER MY_VER_MAJOR,MY_VER_MINOR,MY_VER_BUILD,0 + +#ifdef DEBUG +#define DBG_FL VS_FF_DEBUG +#else +#define DBG_FL 0 +#endif + +#define MY_VERSION_INFO(fileType, descr, intName, origName) \ +LANGUAGE 9, 1 \ +1 VERSIONINFO \ + FILEVERSION MY_VER \ + PRODUCTVERSION MY_VER \ + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK \ + FILEFLAGS DBG_FL \ + FILEOS VOS_NT_WINDOWS32 \ + FILETYPE fileType \ + FILESUBTYPE 0x0L \ +BEGIN \ + BLOCK "StringFileInfo" \ + BEGIN \ + BLOCK "040904b0" \ + BEGIN \ + VALUE "CompanyName", "Igor Pavlov" \ + VALUE "FileDescription", descr \ + VALUE "FileVersion", MY_VERSION \ + VALUE "InternalName", intName \ + VALUE "LegalCopyright", MY_COPYRIGHT \ + VALUE "OriginalFilename", origName \ + VALUE "ProductName", "7-Zip" \ + VALUE "ProductVersion", MY_VERSION \ + END \ + END \ + BLOCK "VarFileInfo" \ + BEGIN \ + VALUE "Translation", 0x409, 1200 \ + END \ +END + +#define MY_VERSION_INFO_APP(descr, intName) MY_VERSION_INFO(VFT_APP, descr, intName, intName ".exe") + +#define MY_VERSION_INFO_DLL(descr, intName) MY_VERSION_INFO(VFT_DLL, descr, intName, intName ".dll") diff --git a/3rdparty/physfs/lzma/CPP/7zip/PropID.h b/3rdparty/physfs/lzma/CPP/7zip/PropID.h new file mode 100644 index 0000000..a13fe27 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/PropID.h @@ -0,0 +1,60 @@ +// Interface/PropID.h + +#ifndef __INTERFACE_PROPID_H +#define __INTERFACE_PROPID_H + +enum +{ + kpidNoProperty = 0, + + kpidHandlerItemIndex = 2, + kpidPath, + kpidName, + kpidExtension, + kpidIsFolder, + kpidSize, + kpidPackedSize, + kpidAttributes, + kpidCreationTime, + kpidLastAccessTime, + kpidLastWriteTime, + kpidSolid, + kpidCommented, + kpidEncrypted, + kpidSplitBefore, + kpidSplitAfter, + kpidDictionarySize, + kpidCRC, + kpidType, + kpidIsAnti, + kpidMethod, + kpidHostOS, + kpidFileSystem, + kpidUser, + kpidGroup, + kpidBlock, + kpidComment, + kpidPosition, + kpidPrefix, + kpidNumSubFolders, + kpidNumSubFiles, + kpidUnpackVer, + kpidVolume, + kpidIsVolume, + kpidOffset, + kpidLinks, + kpidNumBlocks, + kpidNumVolumes, + + kpidTotalSize = 0x1100, + kpidFreeSpace, + kpidClusterSize, + kpidVolumeName, + + kpidLocalName = 0x1200, + kpidProvider, + + kpidUserDefined = 0x10000 +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp new file mode 100644 index 0000000..fce1c4f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp @@ -0,0 +1,880 @@ +// Client7z.cpp + +#include "StdAfx.h" + +#include "Common/MyInitGuid.h" +#include "Common/StringConvert.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" +#include "Windows/PropVariantConversions.h" +#include "Windows/DLL.h" +#include "Windows/FileDir.h" +#include "Windows/FileName.h" +#include "Windows/FileFind.h" + +#include "../../Common/FileStreams.h" +#include "../../Archive/IArchive.h" +#include "../../IPassword.h" +#include "../../MyVersion.h" + + +// {23170F69-40C1-278A-1000-000110070000} +DEFINE_GUID(CLSID_CFormat7z, + 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00); + +using namespace NWindows; + +#define kDllName "7z.dll" + +static const char *kCopyrightString = MY_7ZIP_VERSION +" (" kDllName " client) " +MY_COPYRIGHT " " MY_DATE; + +static const char *kHelpString = +"Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n" +"Examples:\n" +" Client7z.exe a archive.7z f1.txt f2.txt : compress two files to archive.7z\n" +" Client7z.exe l archive.7z : List contents of archive.7z\n" +" Client7z.exe x archive.7z : eXtract files from archive.7z\n"; + + +typedef UINT32 (WINAPI * CreateObjectFunc)( + const GUID *clsID, + const GUID *interfaceID, + void **outObject); + +#ifdef _WIN32 +#ifndef _UNICODE +bool g_IsNT = false; +static inline bool IsItWindowsNT() +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + if (!::GetVersionEx(&versionInfo)) + return false; + return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); +} +#endif +#endif + +void PrintString(const UString &s) +{ + printf("%s", (LPCSTR)GetOemString(s)); +} + +void PrintString(const AString &s) +{ + printf("%s", (LPCSTR)s); +} + +void PrintNewLine() +{ + PrintString("\n"); +} + +void PrintStringLn(const AString &s) +{ + PrintString(s); + PrintNewLine(); +} + +void PrintError(const AString &s) +{ + PrintNewLine(); + PrintString(s); + PrintNewLine(); +} + +static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) +{ + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, propID, &prop)); + if(prop.vt == VT_BOOL) + result = VARIANT_BOOLToBool(prop.boolVal); + else if (prop.vt == VT_EMPTY) + result = false; + else + return E_FAIL; + return S_OK; +} + +static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) +{ + return IsArchiveItemProp(archive, index, kpidIsFolder, result); +} + + +static const wchar_t *kEmptyFileAlias = L"[Content]"; + + +////////////////////////////////////////////////////////////// +// Archive Open callback class + + +class CArchiveOpenCallback: + public IArchiveOpenCallback, + public ICryptoGetTextPassword, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(ICryptoGetTextPassword) + + STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); + STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); + + STDMETHOD(CryptoGetTextPassword)(BSTR *password); + + bool PasswordIsDefined; + UString Password; + + CArchiveOpenCallback() : PasswordIsDefined(false) {} +}; + +STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) +{ + return S_OK; +} + +STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */) +{ + return S_OK; +} + +STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password) +{ + if (!PasswordIsDefined) + { + // You can ask real password here from user + // Password = GetPassword(OutStream); + // PasswordIsDefined = true; + PrintError("Password is not defined"); + return E_ABORT; + } + CMyComBSTR tempName(Password); + *password = tempName.Detach(); + return S_OK; +} + + +////////////////////////////////////////////////////////////// +// Archive Extracting callback class + +static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file "; + +static const char *kTestingString = "Testing "; +static const char *kExtractingString = "Extracting "; +static const char *kSkippingString = "Skipping "; + +static const char *kUnsupportedMethod = "Unsupported Method"; +static const char *kCRCFailed = "CRC Failed"; +static const char *kDataError = "Data Error"; +static const char *kUnknownError = "Unknown Error"; + +class CArchiveExtractCallback: + public IArchiveExtractCallback, + public ICryptoGetTextPassword, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(ICryptoGetTextPassword) + + // IProgress + STDMETHOD(SetTotal)(UInt64 size); + STDMETHOD(SetCompleted)(const UInt64 *completeValue); + + // IArchiveExtractCallback + STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode); + STDMETHOD(PrepareOperation)(Int32 askExtractMode); + STDMETHOD(SetOperationResult)(Int32 resultEOperationResult); + + // ICryptoGetTextPassword + STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword); + +private: + CMyComPtr _archiveHandler; + UString _directoryPath; // Output directory + UString _filePath; // name inside arcvhive + UString _diskFilePath; // full path to file on disk + bool _extractMode; + struct CProcessedFileInfo + { + FILETIME UTCLastWriteTime; + UInt32 Attributes; + bool IsDirectory; + bool AttributesAreDefined; + bool UTCLastWriteTimeIsDefined; + } _processedFileInfo; + + COutFileStream *_outFileStreamSpec; + CMyComPtr _outFileStream; + +public: + void Init(IInArchive *archiveHandler, const UString &directoryPath); + + UInt64 NumErrors; + bool PasswordIsDefined; + UString Password; + + CArchiveExtractCallback() : PasswordIsDefined(false) {} +}; + +void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath) +{ + NumErrors = 0; + _archiveHandler = archiveHandler; + _directoryPath = directoryPath; + NFile::NName::NormalizeDirPathPrefix(_directoryPath); +} + +STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */) +{ + return S_OK; +} + +STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */) +{ + return S_OK; +} + +STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, + ISequentialOutStream **outStream, Int32 askExtractMode) +{ + *outStream = 0; + _outFileStream.Release(); + + { + // Get Name + NCOM::CPropVariant propVariant; + RINOK(_archiveHandler->GetProperty(index, kpidPath, &propVariant)); + + UString fullPath; + if(propVariant.vt == VT_EMPTY) + fullPath = kEmptyFileAlias; + else + { + if(propVariant.vt != VT_BSTR) + return E_FAIL; + fullPath = propVariant.bstrVal; + } + _filePath = fullPath; + } + + if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) + return S_OK; + + { + // Get Attributes + NCOM::CPropVariant propVariant; + RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &propVariant)); + if (propVariant.vt == VT_EMPTY) + { + _processedFileInfo.Attributes = 0; + _processedFileInfo.AttributesAreDefined = false; + } + else + { + if (propVariant.vt != VT_UI4) + throw "incorrect item"; + _processedFileInfo.Attributes = propVariant.ulVal; + _processedFileInfo.AttributesAreDefined = true; + } + } + + RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory)); + + { + // Get Modified Time + NCOM::CPropVariant propVariant; + RINOK(_archiveHandler->GetProperty(index, kpidLastWriteTime, &propVariant)); + _processedFileInfo.UTCLastWriteTimeIsDefined = false; + switch(propVariant.vt) + { + case VT_EMPTY: + // _processedFileInfo.UTCLastWriteTime = _utcLastWriteTimeDefault; + break; + case VT_FILETIME: + _processedFileInfo.UTCLastWriteTime = propVariant.filetime; + _processedFileInfo.UTCLastWriteTimeIsDefined = true; + break; + default: + return E_FAIL; + } + + } + { + // Get Size + NCOM::CPropVariant propVariant; + RINOK(_archiveHandler->GetProperty(index, kpidSize, &propVariant)); + bool newFileSizeDefined = (propVariant.vt != VT_EMPTY); + UInt64 newFileSize; + if (newFileSizeDefined) + newFileSize = ConvertPropVariantToUInt64(propVariant); + } + + + { + // Create folders for file + int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR); + if (slashPos >= 0) + NFile::NDirectory::CreateComplexDirectory(_directoryPath + _filePath.Left(slashPos)); + } + + UString fullProcessedPath = _directoryPath + _filePath; + _diskFilePath = fullProcessedPath; + + if (_processedFileInfo.IsDirectory) + { + NFile::NDirectory::CreateComplexDirectory(fullProcessedPath); + } + else + { + NFile::NFind::CFileInfoW fileInfo; + if(NFile::NFind::FindFile(fullProcessedPath, fileInfo)) + { + if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) + { + PrintString(UString(kCantDeleteOutputFile) + fullProcessedPath); + return E_ABORT; + } + } + + _outFileStreamSpec = new COutFileStream; + CMyComPtr outStreamLoc(_outFileStreamSpec); + if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS)) + { + PrintString((UString)L"can not open output file " + fullProcessedPath); + return E_ABORT; + } + _outFileStream = outStreamLoc; + *outStream = outStreamLoc.Detach(); + } + return S_OK; +} + +STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) +{ + _extractMode = false; + switch (askExtractMode) + { + case NArchive::NExtract::NAskMode::kExtract: + _extractMode = true; + }; + switch (askExtractMode) + { + case NArchive::NExtract::NAskMode::kExtract: + PrintString(kExtractingString); + break; + case NArchive::NExtract::NAskMode::kTest: + PrintString(kTestingString); + break; + case NArchive::NExtract::NAskMode::kSkip: + PrintString(kSkippingString); + break; + }; + PrintString(_filePath); + return S_OK; +} + +STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) +{ + switch(operationResult) + { + case NArchive::NExtract::NOperationResult::kOK: + break; + default: + { + NumErrors++; + PrintString(" "); + switch(operationResult) + { + case NArchive::NExtract::NOperationResult::kUnSupportedMethod: + PrintString(kUnsupportedMethod); + break; + case NArchive::NExtract::NOperationResult::kCRCError: + PrintString(kCRCFailed); + break; + case NArchive::NExtract::NOperationResult::kDataError: + PrintString(kDataError); + break; + default: + PrintString(kUnknownError); + } + } + } + + if (_outFileStream != NULL) + { + if (_processedFileInfo.UTCLastWriteTimeIsDefined) + _outFileStreamSpec->SetLastWriteTime(&_processedFileInfo.UTCLastWriteTime); + RINOK(_outFileStreamSpec->Close()); + } + _outFileStream.Release(); + if (_extractMode && _processedFileInfo.AttributesAreDefined) + NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes); + PrintNewLine(); + return S_OK; +} + + +STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) +{ + if (!PasswordIsDefined) + { + // You can ask real password here from user + // Password = GetPassword(OutStream); + // PasswordIsDefined = true; + PrintError("Password is not defined"); + return E_ABORT; + } + CMyComBSTR tempName(Password); + *password = tempName.Detach(); + return S_OK; +} + + + +////////////////////////////////////////////////////////////// +// Archive Creating callback class + +struct CDirItem +{ + UInt32 Attributes; + FILETIME CreationTime; + FILETIME LastAccessTime; + FILETIME LastWriteTime; + UInt64 Size; + UString Name; + UString FullPath; + bool IsDirectory() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } +}; + +class CArchiveUpdateCallback: + public IArchiveUpdateCallback2, + public ICryptoGetTextPassword2, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2) + + // IProgress + STDMETHOD(SetTotal)(UInt64 size); + STDMETHOD(SetCompleted)(const UInt64 *completeValue); + + // IUpdateCallback2 + STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator); + STDMETHOD(GetUpdateItemInfo)(UInt32 index, + Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive); + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream); + STDMETHOD(SetOperationResult)(Int32 operationResult); + STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size); + STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream); + + STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); + +public: + CRecordVector VolumesSizes; + UString VolName; + UString VolExt; + + UString DirPrefix; + const CObjectVector *DirItems; + + bool PasswordIsDefined; + UString Password; + bool AskPassword; + + bool m_NeedBeClosed; + + UStringVector FailedFiles; + CRecordVector FailedCodes; + + CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {}; + + ~CArchiveUpdateCallback() { Finilize(); } + HRESULT Finilize(); + + void Init(const CObjectVector *dirItems) + { + DirItems = dirItems; + m_NeedBeClosed = false; + FailedFiles.Clear(); + FailedCodes.Clear(); + } +}; + +STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */) +{ + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */) +{ + return S_OK; +} + + +STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */, + Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive) +{ + if(newData != NULL) + *newData = BoolToInt(true); + if(newProperties != NULL) + *newProperties = BoolToInt(true); + if(indexInArchive != NULL) + *indexInArchive = UInt32(-1); + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant propVariant; + + if (propID == kpidIsAnti) + { + propVariant = false; + propVariant.Detach(value); + return S_OK; + } + + { + const CDirItem &dirItem = (*DirItems)[index]; + switch(propID) + { + case kpidPath: + propVariant = dirItem.Name; + break; + case kpidIsFolder: + propVariant = dirItem.IsDirectory(); + break; + case kpidSize: + propVariant = dirItem.Size; + break; + case kpidAttributes: + propVariant = dirItem.Attributes; + break; + case kpidLastAccessTime: + propVariant = dirItem.LastAccessTime; + break; + case kpidCreationTime: + propVariant = dirItem.CreationTime; + break; + case kpidLastWriteTime: + propVariant = dirItem.LastWriteTime; + break; + } + } + propVariant.Detach(value); + return S_OK; +} + +HRESULT CArchiveUpdateCallback::Finilize() +{ + if (m_NeedBeClosed) + { + PrintNewLine(); + m_NeedBeClosed = false; + } + return S_OK; +} + +static void GetStream2(const wchar_t *name) +{ + PrintString("Compressing "); + if (name[0] == 0) + name = kEmptyFileAlias; + PrintString(name); +} + +STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) +{ + RINOK(Finilize()); + + const CDirItem &dirItem = (*DirItems)[index]; + GetStream2(dirItem.Name); + + if(dirItem.IsDirectory()) + return S_OK; + + { + CInFileStream *inStreamSpec = new CInFileStream; + CMyComPtr inStreamLoc(inStreamSpec); + UString path = DirPrefix + dirItem.FullPath; + if(!inStreamSpec->Open(path)) + { + DWORD sysError = ::GetLastError(); + FailedCodes.Add(sysError); + FailedFiles.Add(path); + // if (systemError == ERROR_SHARING_VIOLATION) + { + PrintNewLine(); + PrintError("WARNING: can't open file"); + // PrintString(NError::MyFormatMessageW(systemError)); + return S_FALSE; + } + // return sysError; + } + *inStream = inStreamLoc.Detach(); + } + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */) +{ + m_NeedBeClosed = true; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) +{ + if (VolumesSizes.Size() == 0) + return S_FALSE; + if (index >= (UInt32)VolumesSizes.Size()) + index = VolumesSizes.Size() - 1; + *size = VolumesSizes[index]; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) +{ + wchar_t temp[32]; + ConvertUInt64ToString(index + 1, temp); + UString res = temp; + while (res.Length() < 2) + res = UString(L'0') + res; + UString fileName = VolName; + fileName += L'.'; + fileName += res; + fileName += VolExt; + COutFileStream *streamSpec = new COutFileStream; + CMyComPtr streamLoc(streamSpec); + if(!streamSpec->Create(fileName, false)) + return ::GetLastError(); + *volumeStream = streamLoc.Detach(); + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) +{ + if (!PasswordIsDefined) + { + if (AskPassword) + { + // You can ask real password here from user + // Password = GetPassword(OutStream); + // PasswordIsDefined = true; + PrintError("Password is not defined"); + return E_ABORT; + } + } + *passwordIsDefined = BoolToInt(PasswordIsDefined); + CMyComBSTR tempName(Password); + *password = tempName.Detach(); + return S_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +// Main function + +int +#ifdef _MSC_VER +__cdecl +#endif +main(int argc, char* argv[]) +{ + #ifdef _WIN32 + #ifndef _UNICODE + g_IsNT = IsItWindowsNT(); + #endif + #endif + + PrintStringLn(kCopyrightString); + + if (argc < 3) + { + PrintStringLn(kHelpString); + return 1; + } + NWindows::NDLL::CLibrary library; + if (!library.Load(TEXT(kDllName))) + { + PrintError("Can not load library"); + return 1; + } + CreateObjectFunc createObjectFunc = (CreateObjectFunc)library.GetProcAddress("CreateObject"); + if (createObjectFunc == 0) + { + PrintError("Can not get CreateObject"); + return 1; + } + + AString command = argv[1]; + UString archiveName = GetUnicodeString(argv[2], CP_OEMCP); + if (command.CompareNoCase("a") == 0) + { + // create archive command + if (argc < 4) + { + PrintStringLn(kHelpString); + return 1; + } + CObjectVector dirItems; + int i; + for (i = 3; i < argc; i++) + { + CDirItem item; + UString name = GetUnicodeString(argv[i], CP_OEMCP); + + NFile::NFind::CFileInfoW fileInfo; + if (!NFile::NFind::FindFile(name, fileInfo)) + { + PrintString(UString(L"Can't find file") + name); + return 1; + } + + item.Attributes = fileInfo.Attributes; + item.Size = fileInfo.Size; + item.CreationTime = fileInfo.CreationTime; + item.LastAccessTime = fileInfo.LastAccessTime; + item.LastWriteTime = fileInfo.LastWriteTime; + item.Name = name; + item.FullPath = name; + dirItems.Add(item); + } + COutFileStream *outFileStreamSpec = new COutFileStream; + CMyComPtr outFileStream = outFileStreamSpec; + if (!outFileStreamSpec->Create(archiveName, false)) + { + PrintError("can't create archive file"); + return 1; + } + + CMyComPtr outArchive; + if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK) + { + PrintError("Can not get class object"); + return 1; + } + + CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; + CMyComPtr updateCallback(updateCallbackSpec); + updateCallbackSpec->Init(&dirItems); + // updateCallbackSpec->PasswordIsDefined = true; + // updateCallbackSpec->Password = L"1"; + + HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback); + updateCallbackSpec->Finilize(); + if (result != S_OK) + { + PrintError("Update Error"); + return 1; + } + for (i = 0; i < updateCallbackSpec->FailedFiles.Size(); i++) + { + PrintNewLine(); + PrintString((UString)L"Error for file: " + updateCallbackSpec->FailedFiles[i]); + } + if (updateCallbackSpec->FailedFiles.Size() != 0) + return 1; + } + else + { + if (argc != 3) + { + PrintStringLn(kHelpString); + return 1; + } + + bool listCommand; + if (command.CompareNoCase("l") == 0) + listCommand = true; + else if (command.CompareNoCase("x") == 0) + listCommand = false; + else + { + PrintError("incorrect command"); + return 1; + } + + CMyComPtr archive; + if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK) + { + PrintError("Can not get class object"); + return 1; + } + + CInFileStream *fileSpec = new CInFileStream; + CMyComPtr file = fileSpec; + + if (!fileSpec->Open(archiveName)) + { + PrintError("Can not open archive file"); + return 1; + } + + { + CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback; + CMyComPtr openCallback(openCallbackSpec); + openCallbackSpec->PasswordIsDefined = false; + // openCallbackSpec->PasswordIsDefined = true; + // openCallbackSpec->Password = L"1"; + + if (archive->Open(file, 0, openCallback) != S_OK) + { + PrintError("Can not open archive"); + return 1; + } + } + + if (listCommand) + { + // List command + UInt32 numItems = 0; + archive->GetNumberOfItems(&numItems); + for (UInt32 i = 0; i < numItems; i++) + { + { + // Get uncompressed size of file + NWindows::NCOM::CPropVariant propVariant; + archive->GetProperty(i, kpidSize, &propVariant); + UString s = ConvertPropVariantToString(propVariant); + PrintString(s); + PrintString(" "); + } + { + // Get name of file + NWindows::NCOM::CPropVariant propVariant; + archive->GetProperty(i, kpidPath, &propVariant); + UString s = ConvertPropVariantToString(propVariant); + PrintString(s); + } + PrintString("\n"); + } + } + else + { + // Extract command + CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; + CMyComPtr extractCallback(extractCallbackSpec); + extractCallbackSpec->Init(archive, L""); // second parameter is output folder path + extractCallbackSpec->PasswordIsDefined = false; + // extractCallbackSpec->PasswordIsDefined = true; + // extractCallbackSpec->Password = L"1"; + HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback); + if (result != S_OK) + { + PrintError("Extract Error"); + return 1; + } + } + } + return 0; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsp b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsp new file mode 100644 index 0000000..542e285 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsp @@ -0,0 +1,226 @@ +# Microsoft Developer Studio Project File - Name="Client7z" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Client7z - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Client7z.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Client7z.mak" CFG="Client7z - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Client7z - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Client7z - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Client7z - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "Client7z - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Client7z - Win32 Release" +# Name "Client7z - Win32 Debug" +# Begin Group "Spec" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Windows" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Windows\DLL.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\DLL.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileDir.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileDir.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileFind.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileFind.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileIO.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileName.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\FileName.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariant.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariant.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariantConversions.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Windows\PropVariantConversions.h +# End Source File +# End Group +# Begin Group "Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\IntToString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyString.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\MyVector.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\NewHandler.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\StringConvert.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Wildcard.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Common\Wildcard.h +# End Source File +# End Group +# Begin Group "7zip Common" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\Common\FileStreams.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\FileStreams.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\Client7z.cpp +# End Source File +# End Target +# End Project diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsw b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsw new file mode 100644 index 0000000..598a6d3 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Client7z"=.\Client7z.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.h new file mode 100644 index 0000000..b23436e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include +#include + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/makefile b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/makefile new file mode 100644 index 0000000..226c36a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Client7z/makefile @@ -0,0 +1,45 @@ +PROG = 7z.exe +LIBS = $(LIBS) user32.lib oleaut32.lib advapi32.lib +CFLAGS = $(CFLAGS) -I ../../../ + +CONSOLE_OBJS = \ + $O\Client7z.obj \ + +COMMON_OBJS = \ + $O\IntToString.obj \ + $O\NewHandler.obj \ + $O\MyString.obj \ + $O\StringConvert.obj \ + $O\StringToInt.obj \ + $O\MyVector.obj \ + $O\Wildcard.obj \ + +WIN_OBJS = \ + $O\DLL.obj \ + $O\FileDir.obj \ + $O\FileFind.obj \ + $O\FileIO.obj \ + $O\FileName.obj \ + $O\PropVariant.obj \ + $O\PropVariantConversions.obj \ + +7ZIP_COMMON_OBJS = \ + $O\FileStreams.obj \ + +OBJS = \ + $O\StdAfx.obj \ + $(CONSOLE_OBJS) \ + $(COMMON_OBJS) \ + $(WIN_OBJS) \ + $(7ZIP_COMMON_OBJS) \ + +!include "../../../Build.mak" + +$(CONSOLE_OBJS): $(*B).cpp + $(COMPL) +$(COMMON_OBJS): ../../../Common/$(*B).cpp + $(COMPL) +$(WIN_OBJS): ../../../Windows/$(*B).cpp + $(COMPL) +$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp + $(COMPL) diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp new file mode 100644 index 0000000..6cf95f2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -0,0 +1,1003 @@ +// ArchiveCommandLine.cpp + +#include "StdAfx.h" + +#ifdef _WIN32 +#include +#endif +#include + +#include "Common/ListFileUtils.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/FileName.h" +#include "Windows/FileDir.h" +#ifdef _WIN32 +#include "Windows/FileMapping.h" +#include "Windows/Synchronization.h" +#endif + +#include "ArchiveCommandLine.h" +#include "UpdateAction.h" +#include "Update.h" +#include "SortUtils.h" +#include "EnumDirItems.h" + +extern bool g_CaseSensitive; + +#if _MSC_VER >= 1400 +#define MY_isatty_fileno(x) _isatty(_fileno(x)) +#else +#define MY_isatty_fileno(x) isatty(fileno(x)) +#endif + +#define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); + +using namespace NCommandLineParser; +using namespace NWindows; +using namespace NFile; + +namespace NKey { +enum Enum +{ + kHelp1 = 0, + kHelp2, + kHelp3, + kDisableHeaders, + kDisablePercents, + kArchiveType, + kYes, + kPassword, + kProperty, + kOutputDir, + kWorkingDir, + kInclude, + kExclude, + kArInclude, + kArExclude, + kNoArName, + kUpdate, + kVolume, + kRecursed, + kSfx, + kStdIn, + kStdOut, + kOverwrite, + kEmail, + kShowDialog, + kLargePages, + kCharSet, + kTechMode, + kShareForWrite, + kCaseSensitive +}; + +} + + +static const wchar_t kRecursedIDChar = 'R'; +static const wchar_t *kRecursedPostCharSet = L"0-"; + +namespace NRecursedPostCharIndex { + enum EEnum + { + kWildCardRecursionOnly = 0, + kNoRecursion = 1 + }; +} + +static const char kImmediateNameID = '!'; +static const char kMapNameID = '#'; +static const char kFileListID = '@'; + +static const char kSomeCludePostStringMinSize = 2; // at least <@|!>ame must be +static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!>ame must be + +static const wchar_t *kOverwritePostCharSet = L"asut"; + +NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = +{ + NExtract::NOverwriteMode::kWithoutPrompt, + NExtract::NOverwriteMode::kSkipExisting, + NExtract::NOverwriteMode::kAutoRename, + NExtract::NOverwriteMode::kAutoRenameExisting +}; + +static const CSwitchForm kSwitchForms[] = + { + { L"?", NSwitchType::kSimple, false }, + { L"H", NSwitchType::kSimple, false }, + { L"-HELP", NSwitchType::kSimple, false }, + { L"BA", NSwitchType::kSimple, false }, + { L"BD", NSwitchType::kSimple, false }, + { L"T", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"Y", NSwitchType::kSimple, false }, + { L"P", NSwitchType::kUnLimitedPostString, false, 0 }, + { L"M", NSwitchType::kUnLimitedPostString, true, 1 }, + { L"O", NSwitchType::kUnLimitedPostString, false, 1 }, + { L"W", NSwitchType::kUnLimitedPostString, false, 0 }, + { L"I", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, + { L"X", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, + { L"AI", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, + { L"AX", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, + { L"AN", NSwitchType::kSimple, false }, + { L"U", NSwitchType::kUnLimitedPostString, true, 1}, + { L"V", NSwitchType::kUnLimitedPostString, true, 1}, + { L"R", NSwitchType::kPostChar, false, 0, 0, kRecursedPostCharSet }, + { L"SFX", NSwitchType::kUnLimitedPostString, false, 0 }, + { L"SI", NSwitchType::kUnLimitedPostString, false, 0 }, + { L"SO", NSwitchType::kSimple, false, 0 }, + { L"AO", NSwitchType::kPostChar, false, 1, 1, kOverwritePostCharSet}, + { L"SEML", NSwitchType::kUnLimitedPostString, false, 0}, + { L"AD", NSwitchType::kSimple, false }, + { L"SLP", NSwitchType::kUnLimitedPostString, false, 0}, + { L"SCS", NSwitchType::kUnLimitedPostString, false, 0}, + { L"SLT", NSwitchType::kSimple, false }, + { L"SSW", NSwitchType::kSimple, false }, + { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" } + }; + +static const CCommandForm g_CommandForms[] = +{ + { L"A", false }, + { L"U", false }, + { L"D", false }, + { L"T", false }, + { L"E", false }, + { L"X", false }, + { L"L", false }, + { L"B", false }, + { L"I", false } +}; + +static const int kNumCommandForms = sizeof(g_CommandForms) / sizeof(g_CommandForms[0]); + +static const wchar_t *kUniversalWildcard = L"*"; +static const int kMinNonSwitchWords = 1; +static const int kCommandIndex = 0; + +// --------------------------- +// exception messages + +static const char *kUserErrorMessage = "Incorrect command line"; +static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; +static const char *kIncorrectWildCardInListFile = "Incorrect wildcard in listfile"; +static const char *kIncorrectWildCardInCommandLine = "Incorrect wildcard in command line"; +static const char *kTerminalOutError = "I won't write compressed data to a terminal"; +static const char *kSameTerminalError = "I won't write data and program's messages to same terminal"; + +static void ThrowException(const char *errorMessage) +{ + throw CArchiveCommandLineException(errorMessage); +}; + +static void ThrowUserErrorException() +{ + ThrowException(kUserErrorMessage); +}; + +// --------------------------- + +bool CArchiveCommand::IsFromExtractGroup() const +{ + switch(CommandType) + { + case NCommandType::kTest: + case NCommandType::kExtract: + case NCommandType::kFullExtract: + return true; + default: + return false; + } +} + +NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const +{ + switch(CommandType) + { + case NCommandType::kTest: + case NCommandType::kFullExtract: + return NExtract::NPathMode::kFullPathnames; + default: + return NExtract::NPathMode::kNoPathnames; + } +} + +bool CArchiveCommand::IsFromUpdateGroup() const +{ + return (CommandType == NCommandType::kAdd || + CommandType == NCommandType::kUpdate || + CommandType == NCommandType::kDelete); +} + +static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) +{ + switch (index) + { + case NRecursedPostCharIndex::kWildCardRecursionOnly: + return NRecursedType::kWildCardOnlyRecursed; + case NRecursedPostCharIndex::kNoRecursion: + return NRecursedType::kNonRecursed; + default: + return NRecursedType::kRecursed; + } +} + +static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &command) +{ + UString commandStringUpper = commandString; + commandStringUpper.MakeUpper(); + UString postString; + int commandIndex = ParseCommand(kNumCommandForms, g_CommandForms, commandStringUpper, + postString) ; + if (commandIndex < 0) + return false; + command.CommandType = (NCommandType::EEnum)commandIndex; + return true; +} + +// ------------------------------------------------------------------ +// filenames functions + +static bool AddNameToCensor(NWildcard::CCensor &wildcardCensor, + const UString &name, bool include, NRecursedType::EEnum type) +{ + bool isWildCard = DoesNameContainWildCard(name); + bool recursed = false; + + switch (type) + { + case NRecursedType::kWildCardOnlyRecursed: + recursed = isWildCard; + break; + case NRecursedType::kRecursed: + recursed = true; + break; + case NRecursedType::kNonRecursed: + recursed = false; + break; + } + wildcardCensor.AddItem(include, name, recursed); + return true; +} + +static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor, + LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage) +{ + UStringVector names; + if (!ReadNamesFromListFile(fileName, names, codePage)) + throw kIncorrectListFile; + for (int i = 0; i < names.Size(); i++) + if (!AddNameToCensor(wildcardCensor, names[i], include, type)) + throw kIncorrectWildCardInListFile; +} + +static void AddCommandLineWildCardToCensr(NWildcard::CCensor &wildcardCensor, + const UString &name, bool include, NRecursedType::EEnum recursedType) +{ + if (!AddNameToCensor(wildcardCensor, name, include, recursedType)) + throw kIncorrectWildCardInCommandLine; +} + +static void AddToCensorFromNonSwitchesStrings( + int startIndex, + NWildcard::CCensor &wildcardCensor, + const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, + bool thereAreSwitchIncludes, UINT codePage) +{ + if(nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes)) + AddCommandLineWildCardToCensr(wildcardCensor, kUniversalWildcard, true, type); + for(int i = startIndex; i < nonSwitchStrings.Size(); i++) + { + const UString &s = nonSwitchStrings[i]; + if (s[0] == kFileListID) + AddToCensorFromListFile(wildcardCensor, s.Mid(1), true, type, codePage); + else + AddCommandLineWildCardToCensr(wildcardCensor, s, true, type); + } +} + +#ifdef _WIN32 +static void ParseMapWithPaths(NWildcard::CCensor &wildcardCensor, + const UString &switchParam, bool include, + NRecursedType::EEnum commonRecursedType) +{ + int splitPos = switchParam.Find(L':'); + if (splitPos < 0) + ThrowUserErrorException(); + UString mappingName = switchParam.Left(splitPos); + + UString switchParam2 = switchParam.Mid(splitPos + 1); + splitPos = switchParam2.Find(L':'); + if (splitPos < 0) + ThrowUserErrorException(); + + UString mappingSize = switchParam2.Left(splitPos); + UString eventName = switchParam2.Mid(splitPos + 1); + + UInt64 dataSize64 = ConvertStringToUInt64(mappingSize, NULL); + UInt32 dataSize = (UInt32)dataSize64; + { + CFileMapping fileMapping; + if (!fileMapping.Open(FILE_MAP_READ, false, GetSystemString(mappingName))) + ThrowException("Can not open mapping"); + LPVOID data = fileMapping.MapViewOfFile(FILE_MAP_READ, 0, dataSize); + if (data == NULL) + ThrowException("MapViewOfFile error"); + try + { + const wchar_t *curData = (const wchar_t *)data; + if (*curData != 0) + ThrowException("Incorrect mapping data"); + UInt32 numChars = dataSize / sizeof(wchar_t); + UString name; + for (UInt32 i = 1; i < numChars; i++) + { + wchar_t c = curData[i]; + if (c == L'\0') + { + AddCommandLineWildCardToCensr(wildcardCensor, + name, include, commonRecursedType); + name.Empty(); + } + else + name += c; + } + if (!name.IsEmpty()) + ThrowException("data error"); + } + catch(...) + { + UnmapViewOfFile(data); + throw; + } + UnmapViewOfFile(data); + } + + { + NSynchronization::CManualResetEvent event; + if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK) + event.Set(); + } +} +#endif + +static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, + const UStringVector &strings, bool include, + NRecursedType::EEnum commonRecursedType, UINT codePage) +{ + for(int i = 0; i < strings.Size(); i++) + { + const UString &name = strings[i]; + NRecursedType::EEnum recursedType; + int pos = 0; + if (name.Length() < kSomeCludePostStringMinSize) + ThrowUserErrorException(); + if (::MyCharUpper(name[pos]) == kRecursedIDChar) + { + pos++; + int index = UString(kRecursedPostCharSet).Find(name[pos]); + recursedType = GetRecursedTypeFromIndex(index); + if (index >= 0) + pos++; + } + else + recursedType = commonRecursedType; + if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize) + ThrowUserErrorException(); + UString tail = name.Mid(pos + 1); + if (name[pos] == kImmediateNameID) + AddCommandLineWildCardToCensr(wildcardCensor, tail, include, recursedType); + else if (name[pos] == kFileListID) + AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage); + #ifdef _WIN32 + else if (name[pos] == kMapNameID) + ParseMapWithPaths(wildcardCensor, tail, include, recursedType); + #endif + else + ThrowUserErrorException(); + } +} + +#ifdef _WIN32 + +// This code converts all short file names to long file names. + +static void ConvertToLongName(const UString &prefix, UString &name) +{ + if (name.IsEmpty() || DoesNameContainWildCard(name)) + return; + NFind::CFileInfoW fileInfo; + if (NFind::FindFile(prefix + name, fileInfo)) + name = fileInfo.Name; +} + +static void ConvertToLongNames(const UString &prefix, CObjectVector &items) +{ + for (int i = 0; i < items.Size(); i++) + { + NWildcard::CItem &item = items[i]; + if (item.Recursive || item.PathParts.Size() != 1) + continue; + ConvertToLongName(prefix, item.PathParts.Front()); + } +} + +static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) +{ + ConvertToLongNames(prefix, node.IncludeItems); + ConvertToLongNames(prefix, node.ExcludeItems); + int i; + for (i = 0; i < node.SubNodes.Size(); i++) + ConvertToLongName(prefix, node.SubNodes[i].Name); + // mix folders with same name + for (i = 0; i < node.SubNodes.Size(); i++) + { + NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; + for (int j = i + 1; j < node.SubNodes.Size();) + { + const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; + if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0) + { + nextNode1.IncludeItems += nextNode2.IncludeItems; + nextNode1.ExcludeItems += nextNode2.ExcludeItems; + node.SubNodes.Delete(j); + } + else + j++; + } + } + for (i = 0; i < node.SubNodes.Size(); i++) + { + NWildcard::CCensorNode &nextNode = node.SubNodes[i]; + ConvertToLongNames(prefix + nextNode.Name + wchar_t(NFile::NName::kDirDelimiter), nextNode); + } +} + +static void ConvertToLongNames(NWildcard::CCensor &censor) +{ + for (int i = 0; i < censor.Pairs.Size(); i++) + { + NWildcard::CPair &pair = censor.Pairs[i]; + ConvertToLongNames(pair.Prefix, pair.Head); + } +} + +#endif + +static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) +{ + switch(i) + { + case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; + case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; + case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress; + case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti; + } + throw 98111603; +} + +const UString kUpdatePairStateIDSet = L"PQRXYZW"; +const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; + +const UString kUpdatePairActionIDSet = L"0123"; //Ignore, Copy, Compress, Create Anti + +const wchar_t *kUpdateIgnoreItselfPostStringID = L"-"; +const wchar_t kUpdateNewArchivePostCharID = '!'; + + +static bool ParseUpdateCommandString2(const UString &command, + NUpdateArchive::CActionSet &actionSet, UString &postString) +{ + for(int i = 0; i < command.Length();) + { + wchar_t c = MyCharUpper(command[i]); + int statePos = kUpdatePairStateIDSet.Find(c); + if (statePos < 0) + { + postString = command.Mid(i); + return true; + } + i++; + if (i >= command.Length()) + return false; + int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i])); + if (actionPos < 0) + return false; + actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos); + if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) + return false; + i++; + } + postString.Empty(); + return true; +} + +static void ParseUpdateCommandString(CUpdateOptions &options, + const UStringVector &updatePostStrings, + const NUpdateArchive::CActionSet &defaultActionSet) +{ + for(int i = 0; i < updatePostStrings.Size(); i++) + { + const UString &updateString = updatePostStrings[i]; + if(updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0) + { + if(options.UpdateArchiveItself) + { + options.UpdateArchiveItself = false; + options.Commands.Delete(0); + } + } + else + { + NUpdateArchive::CActionSet actionSet = defaultActionSet; + + UString postString; + if (!ParseUpdateCommandString2(updateString, actionSet, postString)) + ThrowUserErrorException(); + if(postString.IsEmpty()) + { + if(options.UpdateArchiveItself) + options.Commands[0].ActionSet = actionSet; + } + else + { + if(MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID) + ThrowUserErrorException(); + CUpdateArchiveCommand uc; + UString archivePath = postString.Mid(1); + if (archivePath.IsEmpty()) + ThrowUserErrorException(); + uc.UserArchivePath = archivePath; + uc.ActionSet = actionSet; + options.Commands.Add(uc); + } + } + } +} + +static const char kByteSymbol = 'B'; +static const char kKiloSymbol = 'K'; +static const char kMegaSymbol = 'M'; +static const char kGigaSymbol = 'G'; + +static bool ParseComplexSize(const UString &src, UInt64 &result) +{ + UString s = src; + s.MakeUpper(); + + const wchar_t *start = s; + const wchar_t *end; + UInt64 number = ConvertStringToUInt64(start, &end); + int numDigits = (int)(end - start); + if (numDigits == 0 || s.Length() > numDigits + 1) + return false; + if (s.Length() == numDigits) + { + result = number; + return true; + } + int numBits; + switch (s[numDigits]) + { + case kByteSymbol: + result = number; + return true; + case kKiloSymbol: + numBits = 10; + break; + case kMegaSymbol: + numBits = 20; + break; + case kGigaSymbol: + numBits = 30; + break; + default: + return false; + } + if (number >= ((UInt64)1 << (64 - numBits))) + return false; + result = number << numBits; + return true; +} + +static void SetAddCommandOptions( + NCommandType::EEnum commandType, + const CParser &parser, + CUpdateOptions &options) +{ + NUpdateArchive::CActionSet defaultActionSet; + switch(commandType) + { + case NCommandType::kAdd: + defaultActionSet = NUpdateArchive::kAddActionSet; + break; + case NCommandType::kDelete: + defaultActionSet = NUpdateArchive::kDeleteActionSet; + break; + default: + defaultActionSet = NUpdateArchive::kUpdateActionSet; + } + + options.UpdateArchiveItself = true; + + options.Commands.Clear(); + CUpdateArchiveCommand updateMainCommand; + updateMainCommand.ActionSet = defaultActionSet; + options.Commands.Add(updateMainCommand); + if(parser[NKey::kUpdate].ThereIs) + ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, + defaultActionSet); + if(parser[NKey::kWorkingDir].ThereIs) + { + const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; + if (postString.IsEmpty()) + NDirectory::MyGetTempPath(options.WorkingDir); + else + options.WorkingDir = postString; + } + options.SfxMode = parser[NKey::kSfx].ThereIs; + if (options.SfxMode) + options.SfxModule = parser[NKey::kSfx].PostStrings[0]; + + if (parser[NKey::kVolume].ThereIs) + { + const UStringVector &sv = parser[NKey::kVolume].PostStrings; + for (int i = 0; i < sv.Size(); i++) + { + UInt64 size; + if (!ParseComplexSize(sv[i], size)) + ThrowException("Incorrect volume size"); + options.VolumesSizes.Add(size); + } + } +} + +static void SetMethodOptions(const CParser &parser, CObjectVector &properties) +{ + if (parser[NKey::kProperty].ThereIs) + { + // options.MethodMode.Properties.Clear(); + for(int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) + { + CProperty property; + const UString &postString = parser[NKey::kProperty].PostStrings[i]; + int index = postString.Find(L'='); + if (index < 0) + property.Name = postString; + else + { + property.Name = postString.Left(index); + property.Value = postString.Mid(index + 1); + } + properties.Add(property); + } + } +} + +CArchiveCommandLineParser::CArchiveCommandLineParser(): + parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {} + +void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings, + CArchiveCommandLineOptions &options) +{ + try + { + parser.ParseStrings(kSwitchForms, commandStrings); + } + catch(...) + { + ThrowUserErrorException(); + } + + options.IsInTerminal = MY_IS_TERMINAL(stdin); + options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); + options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); + options.StdOutMode = parser[NKey::kStdOut].ThereIs; + options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs; + options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; + + #ifdef _WIN32 + options.LargePages = false; + if (parser[NKey::kLargePages].ThereIs) + { + const UString &postString = parser[NKey::kLargePages].PostStrings.Front(); + if (postString.IsEmpty()) + options.LargePages = true; + } + #endif +} + +struct CCodePagePair +{ + const wchar_t *Name; + UINT CodePage; +}; + +static CCodePagePair g_CodePagePairs[] = +{ + { L"UTF-8", CP_UTF8 }, + { L"WIN", CP_ACP }, + { L"DOS", CP_OEMCP } +}; + +static const int kNumCodePages = sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0]); + +static bool ConvertStringToUInt32(const wchar_t *s, UInt32 &v) +{ + const wchar_t *end; + UInt64 number = ConvertStringToUInt64(s, &end); + if (*end != 0) + return false; + if (number > (UInt32)0xFFFFFFFF) + return false; + v = (UInt32)number; + return true; +} + +void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) +{ + const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; + int numNonSwitchStrings = nonSwitchStrings.Size(); + if(numNonSwitchStrings < kMinNonSwitchWords) + ThrowUserErrorException(); + + if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) + ThrowUserErrorException(); + + options.TechMode = parser[NKey::kTechMode].ThereIs; + + if (parser[NKey::kCaseSensitive].ThereIs) + g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0); + + NRecursedType::EEnum recursedType; + if (parser[NKey::kRecursed].ThereIs) + recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); + else + recursedType = NRecursedType::kNonRecursed; + + UINT codePage = CP_UTF8; + if (parser[NKey::kCharSet].ThereIs) + { + UString name = parser[NKey::kCharSet].PostStrings.Front(); + name.MakeUpper(); + int i; + for (i = 0; i < kNumCodePages; i++) + { + const CCodePagePair &pair = g_CodePagePairs[i]; + if (name.Compare(pair.Name) == 0) + { + codePage = pair.CodePage; + break; + } + } + if (i >= kNumCodePages) + ThrowUserErrorException(); + } + + bool thereAreSwitchIncludes = false; + if (parser[NKey::kInclude].ThereIs) + { + thereAreSwitchIncludes = true; + AddSwitchWildCardsToCensor(options.WildcardCensor, + parser[NKey::kInclude].PostStrings, true, recursedType, codePage); + } + if (parser[NKey::kExclude].ThereIs) + AddSwitchWildCardsToCensor(options.WildcardCensor, + parser[NKey::kExclude].PostStrings, false, recursedType, codePage); + + int curCommandIndex = kCommandIndex + 1; + bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && + options.Command.CommandType != NCommandType::kBenchmark && + options.Command.CommandType != NCommandType::kInfo; + if (thereIsArchiveName) + { + if(curCommandIndex >= numNonSwitchStrings) + ThrowUserErrorException(); + options.ArchiveName = nonSwitchStrings[curCommandIndex++]; + } + + AddToCensorFromNonSwitchesStrings( + curCommandIndex, options.WildcardCensor, + nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage); + + options.YesToAll = parser[NKey::kYes].ThereIs; + + bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); + + options.PasswordEnabled = parser[NKey::kPassword].ThereIs; + + if(options.PasswordEnabled) + options.Password = parser[NKey::kPassword].PostStrings[0]; + + options.StdInMode = parser[NKey::kStdIn].ThereIs; + options.ShowDialog = parser[NKey::kShowDialog].ThereIs; + + if(isExtractGroupCommand || options.Command.CommandType == NCommandType::kList) + { + if (options.StdInMode) + ThrowException("Reading archives from stdin is not implemented"); + if (!options.WildcardCensor.AllAreRelative()) + ThrowException("Cannot use absolute pathnames for this command"); + + NWildcard::CCensor archiveWildcardCensor; + + if (parser[NKey::kArInclude].ThereIs) + { + AddSwitchWildCardsToCensor(archiveWildcardCensor, + parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage); + } + if (parser[NKey::kArExclude].ThereIs) + AddSwitchWildCardsToCensor(archiveWildcardCensor, + parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage); + + if (thereIsArchiveName) + AddCommandLineWildCardToCensr(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed); + + #ifdef _WIN32 + ConvertToLongNames(archiveWildcardCensor); + #endif + + archiveWildcardCensor.ExtendExclude(); + + CObjectVector dirItems; + { + UStringVector errorPaths; + CRecordVector errorCodes; + HRESULT res = EnumerateItems(archiveWildcardCensor, dirItems, NULL, errorPaths, errorCodes); + if (res != S_OK || errorPaths.Size() > 0) + throw "cannot find archive"; + } + UStringVector archivePaths; + int i; + for (i = 0; i < dirItems.Size(); i++) + { + const CDirItem &dirItem = dirItems[i]; + if (!dirItem.IsDirectory()) + archivePaths.Add(dirItem.FullPath); + } + + if (archivePaths.Size() == 0) + throw "there is no such archive"; + + UStringVector archivePathsFull; + + for (i = 0; i < archivePaths.Size(); i++) + { + UString fullPath; + NFile::NDirectory::MyGetFullPathName(archivePaths[i], fullPath); + archivePathsFull.Add(fullPath); + } + CIntVector indices; + SortFileNames(archivePathsFull, indices); + options.ArchivePathsSorted.Reserve(indices.Size()); + options.ArchivePathsFullSorted.Reserve(indices.Size()); + for (i = 0; i < indices.Size(); i++) + { + options.ArchivePathsSorted.Add(archivePaths[indices[i]]); + options.ArchivePathsFullSorted.Add(archivePathsFull[indices[i]]); + } + + if (isExtractGroupCommand) + { + SetMethodOptions(parser, options.ExtractProperties); + if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) + throw kSameTerminalError; + if(parser[NKey::kOutputDir].ThereIs) + { + options.OutputDir = parser[NKey::kOutputDir].PostStrings[0]; + NFile::NName::NormalizeDirPathPrefix(options.OutputDir); + } + + options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore; + if(parser[NKey::kOverwrite].ThereIs) + options.OverwriteMode = + k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; + else if (options.YesToAll) + options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; + } + } + else if(options.Command.IsFromUpdateGroup()) + { + CUpdateOptions &updateOptions = options.UpdateOptions; + + if(parser[NKey::kArchiveType].ThereIs) + options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; + + SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); + + SetMethodOptions(parser, updateOptions.MethodMode.Properties); + + if (parser[NKey::kShareForWrite].ThereIs) + updateOptions.OpenShareForWrite = true; + + options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; + + if (options.EnablePercents) + { + if ((options.StdOutMode && !options.IsStdErrTerminal) || + (!options.StdOutMode && !options.IsStdOutTerminal)) + options.EnablePercents = false; + } + + updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; + if (updateOptions.EMailMode) + { + updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); + if (updateOptions.EMailAddress.Length() > 0) + if (updateOptions.EMailAddress[0] == L'.') + { + updateOptions.EMailRemoveAfter = true; + updateOptions.EMailAddress.Delete(0); + } + } + + updateOptions.StdOutMode = options.StdOutMode; + updateOptions.StdInMode = options.StdInMode; + + if (updateOptions.StdOutMode && updateOptions.EMailMode) + throw "stdout mode and email mode cannot be combined"; + if (updateOptions.StdOutMode && options.IsStdOutTerminal) + throw kTerminalOutError; + if(updateOptions.StdInMode) + updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); + + #ifdef _WIN32 + ConvertToLongNames(options.WildcardCensor); + #endif + } + else if(options.Command.CommandType == NCommandType::kBenchmark) + { + options.NumThreads = (UInt32)-1; + options.DictionarySize = (UInt32)-1; + options.NumIterations = 1; + if (curCommandIndex < numNonSwitchStrings) + { + if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations)) + ThrowUserErrorException(); + } + for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) + { + UString postString = parser[NKey::kProperty].PostStrings[i]; + postString.MakeUpper(); + if (postString.Length() < 2) + ThrowUserErrorException(); + if (postString[0] == 'D') + { + int pos = 1; + if (postString[pos] == '=') + pos++; + UInt32 logSize; + if (!ConvertStringToUInt32((const wchar_t *)postString + pos, logSize)) + ThrowUserErrorException(); + if (logSize > 31) + ThrowUserErrorException(); + options.DictionarySize = 1 << logSize; + } + else if (postString[0] == 'M' && postString[1] == 'T' ) + { + int pos = 2; + if (postString[pos] == '=') + pos++; + if (postString[pos] != 0) + if (!ConvertStringToUInt32((const wchar_t *)postString + pos, options.NumThreads)) + ThrowUserErrorException(); + } + else if (postString[0] == 'M' && postString[1] == '=' ) + { + int pos = 2; + if (postString[pos] != 0) + options.Method = postString.Mid(2); + } + else + ThrowUserErrorException(); + } + } + else if(options.Command.CommandType == NCommandType::kInfo) + { + } + else + ThrowUserErrorException(); + options.WildcardCensor.ExtendExclude(); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h new file mode 100644 index 0000000..5f54b06 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h @@ -0,0 +1,104 @@ +// ArchiveCommandLine.h + +#ifndef __ARCHIVECOMMANDLINE_H +#define __ARCHIVECOMMANDLINE_H + +#include "Common/Wildcard.h" +#include "Common/CommandLineParser.h" + +#include "Extract.h" +#include "Update.h" + +struct CArchiveCommandLineException: public AString +{ + CArchiveCommandLineException(const char *errorMessage): AString(errorMessage) {} +}; + +namespace NCommandType { enum EEnum +{ + kAdd = 0, + kUpdate, + kDelete, + kTest, + kExtract, + kFullExtract, + kList, + kBenchmark, + kInfo +};} + +namespace NRecursedType { enum EEnum +{ + kRecursed, + kWildCardOnlyRecursed, + kNonRecursed +};} + +struct CArchiveCommand +{ + NCommandType::EEnum CommandType; + bool IsFromExtractGroup() const; + bool IsFromUpdateGroup() const; + bool IsTestMode() const { return CommandType == NCommandType::kTest; } + NExtract::NPathMode::EEnum GetPathMode() const; +}; + +struct CArchiveCommandLineOptions +{ + bool HelpMode; + + #ifdef _WIN32 + bool LargePages; + #endif + + bool IsInTerminal; + bool IsStdOutTerminal; + bool IsStdErrTerminal; + bool StdInMode; + bool StdOutMode; + bool EnableHeaders; + + bool YesToAll; + bool ShowDialog; + // NWildcard::CCensor ArchiveWildcardCensor; + NWildcard::CCensor WildcardCensor; + + CArchiveCommand Command; + UString ArchiveName; + + bool PasswordEnabled; + UString Password; + + bool TechMode; + // Extract + bool AppendName; + UString OutputDir; + NExtract::NOverwriteMode::EEnum OverwriteMode; + UStringVector ArchivePathsSorted; + UStringVector ArchivePathsFullSorted; + CObjectVector ExtractProperties; + + CUpdateOptions UpdateOptions; + UString ArcType; + bool EnablePercents; + + // Benchmark + UInt32 NumIterations; + UInt32 NumThreads; + UInt32 DictionarySize; + UString Method; + + + CArchiveCommandLineOptions(): StdInMode(false), StdOutMode(false) {}; +}; + +class CArchiveCommandLineParser +{ + NCommandLineParser::CParser parser; +public: + CArchiveCommandLineParser(); + void Parse1(const UStringVector &commandStrings, CArchiveCommandLineOptions &options); + void Parse2(CArchiveCommandLineOptions &options); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp new file mode 100644 index 0000000..c3913e1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp @@ -0,0 +1,479 @@ +// ArchiveExtractCallback.cpp + +#include "StdAfx.h" + +#include "ArchiveExtractCallback.h" + +#include "Common/Wildcard.h" +#include "Common/StringConvert.h" +#include "Common/ComTry.h" + +#include "Windows/FileDir.h" +#include "Windows/FileFind.h" +#include "Windows/Time.h" +#include "Windows/Defs.h" +#include "Windows/PropVariant.h" + +#include "Windows/PropVariantConversions.h" + +#include "../../Common/FilePathAutoRename.h" + +#include "../Common/ExtractingFilePath.h" +#include "OpenArchive.h" + +using namespace NWindows; + +static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name"; +static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file "; +static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file "; + + +void CArchiveExtractCallback::Init( + IInArchive *archiveHandler, + IFolderArchiveExtractCallback *extractCallback2, + bool stdOutMode, + const UString &directoryPath, + const UStringVector &removePathParts, + const UString &itemDefaultName, + const FILETIME &utcLastWriteTimeDefault, + UInt32 attributesDefault, + UInt64 packSize) +{ + _stdOutMode = stdOutMode; + _numErrors = 0; + _unpTotal = 1; + _packTotal = packSize; + + _extractCallback2 = extractCallback2; + _compressProgress.Release(); + _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); + + LocalProgressSpec->Init(extractCallback2, true); + LocalProgressSpec->SendProgress = false; + + _itemDefaultName = itemDefaultName; + _utcLastWriteTimeDefault = utcLastWriteTimeDefault; + _attributesDefault = attributesDefault; + _removePathParts = removePathParts; + _archiveHandler = archiveHandler; + _directoryPath = directoryPath; + NFile::NName::NormalizeDirPathPrefix(_directoryPath); +} + +STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) +{ + COM_TRY_BEGIN + _unpTotal = size; + if (!_multiArchives && _extractCallback2) + return _extractCallback2->SetTotal(size); + return S_OK; + COM_TRY_END +} + +static void NormalizeVals(UInt64 &v1, UInt64 &v2) +{ + const UInt64 kMax = (UInt64)1 << 31; + while (v1 > kMax) + { + v1 >>= 1; + v2 >>= 1; + } +} + +static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) +{ + NormalizeVals(packTotal, unpTotal); + NormalizeVals(unpCur, unpTotal); + if (unpTotal == 0) + unpTotal = 1; + return unpCur * packTotal / unpTotal; +} + +STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) +{ + COM_TRY_BEGIN + if (!_extractCallback2) + return S_OK; + + if (_multiArchives) + { + if (completeValue != NULL) + { + UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); + return _extractCallback2->SetCompleted(&packCur); + } + } + return _extractCallback2->SetCompleted(completeValue); + COM_TRY_END +} + +STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + COM_TRY_BEGIN + return _localProgress->SetRatioInfo(inSize, outSize); + COM_TRY_END +} + +void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath) +{ + fullPath = _directoryPath; + for(int i = 0; i < dirPathParts.Size(); i++) + { + if (i > 0) + fullPath += wchar_t(NFile::NName::kDirDelimiter); + fullPath += dirPathParts[i]; + NFile::NDirectory::MyCreateDirectory(fullPath); + } +} + +static UString MakePathNameFromParts(const UStringVector &parts) +{ + UString result; + for(int i = 0; i < parts.Size(); i++) + { + if(i != 0) + result += wchar_t(NFile::NName::kDirDelimiter); + result += parts[i]; + } + return result; +} + + +HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) +{ + filetimeIsDefined = false; + NCOM::CPropVariant prop; + RINOK(_archiveHandler->GetProperty(index, propID, &prop)); + if (prop.vt == VT_FILETIME) + { + filetime = prop.filetime; + filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); + } + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; +} + +STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) +{ + COM_TRY_BEGIN + *outStream = 0; + _outFileStream.Release(); + + _encrypted = false; + _isSplit = false; + _curSize = 0; + + UString fullPath; + + RINOK(GetArchiveItemPath(_archiveHandler, index, _itemDefaultName, fullPath)); + RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory)); + + _filePath = fullPath; + + { + NCOM::CPropVariant prop; + RINOK(_archiveHandler->GetProperty(index, kpidPosition, &prop)); + if (prop.vt != VT_EMPTY) + { + if (prop.vt != VT_UI8) + return E_FAIL; + _position = prop.uhVal.QuadPart; + _isSplit = true; + } + } + + RINOK(IsArchiveItemProp(_archiveHandler, index, kpidEncrypted, _encrypted)); + + bool newFileSizeDefined; + UInt64 newFileSize; + { + NCOM::CPropVariant prop; + RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop)); + newFileSizeDefined = (prop.vt != VT_EMPTY); + if (newFileSizeDefined) + { + newFileSize = ConvertPropVariantToUInt64(prop); + _curSize = newFileSize; + } + } + + if(askExtractMode == NArchive::NExtract::NAskMode::kExtract) + { + if (_stdOutMode) + { + CMyComPtr outStreamLoc = new CStdOutFileStream; + *outStream = outStreamLoc.Detach(); + return S_OK; + } + + { + NCOM::CPropVariant prop; + RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &prop)); + if (prop.vt == VT_EMPTY) + { + _processedFileInfo.Attributes = _attributesDefault; + _processedFileInfo.AttributesAreDefined = false; + } + else + { + if (prop.vt != VT_UI4) + return E_FAIL; + _processedFileInfo.Attributes = prop.ulVal; + _processedFileInfo.AttributesAreDefined = true; + } + } + + RINOK(GetTime(index, kpidCreationTime, _processedFileInfo.CreationTime, + _processedFileInfo.IsCreationTimeDefined)); + RINOK(GetTime(index, kpidLastWriteTime, _processedFileInfo.LastWriteTime, + _processedFileInfo.IsLastWriteTimeDefined)); + RINOK(GetTime(index, kpidLastAccessTime, _processedFileInfo.LastAccessTime, + _processedFileInfo.IsLastAccessTimeDefined)); + + bool isAnti = false; + RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti)); + + UStringVector pathParts; + SplitPathToParts(fullPath, pathParts); + + if(pathParts.IsEmpty()) + return E_FAIL; + int numRemovePathParts = 0; + switch(_pathMode) + { + case NExtract::NPathMode::kFullPathnames: + break; + case NExtract::NPathMode::kCurrentPathnames: + { + numRemovePathParts = _removePathParts.Size(); + if (pathParts.Size() <= numRemovePathParts) + return E_FAIL; + for (int i = 0; i < numRemovePathParts; i++) + if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) + return E_FAIL; + break; + } + case NExtract::NPathMode::kNoPathnames: + { + numRemovePathParts = pathParts.Size() - 1; + break; + } + } + pathParts.Delete(0, numRemovePathParts); + MakeCorrectPath(pathParts); + UString processedPath = MakePathNameFromParts(pathParts); + if (!isAnti) + { + if (!_processedFileInfo.IsDirectory) + { + if (!pathParts.IsEmpty()) + pathParts.DeleteBack(); + } + + if (!pathParts.IsEmpty()) + { + UString fullPathNew; + CreateComplexDirectory(pathParts, fullPathNew); + if (_processedFileInfo.IsDirectory) + NFile::NDirectory::SetDirTime(fullPathNew, + (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL, + (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL, + (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault); + } + } + + + UString fullProcessedPath = _directoryPath + processedPath; + + if(_processedFileInfo.IsDirectory) + { + _diskFilePath = fullProcessedPath; + if (isAnti) + NFile::NDirectory::MyRemoveDirectory(_diskFilePath); + return S_OK; + } + + if (!_isSplit) + { + NFile::NFind::CFileInfoW fileInfo; + if(NFile::NFind::FindFile(fullProcessedPath, fileInfo)) + { + switch(_overwriteMode) + { + case NExtract::NOverwriteMode::kSkipExisting: + return S_OK; + case NExtract::NOverwriteMode::kAskBefore: + { + Int32 overwiteResult; + RINOK(_extractCallback2->AskOverwrite( + fullProcessedPath, &fileInfo.LastWriteTime, &fileInfo.Size, fullPath, + _processedFileInfo.IsLastWriteTimeDefined ? &_processedFileInfo.LastWriteTime : NULL, + newFileSizeDefined ? &newFileSize : NULL, + &overwiteResult)) + + switch(overwiteResult) + { + case NOverwriteAnswer::kCancel: + return E_ABORT; + case NOverwriteAnswer::kNo: + return S_OK; + case NOverwriteAnswer::kNoToAll: + _overwriteMode = NExtract::NOverwriteMode::kSkipExisting; + return S_OK; + case NOverwriteAnswer::kYesToAll: + _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; + break; + case NOverwriteAnswer::kYes: + break; + case NOverwriteAnswer::kAutoRename: + _overwriteMode = NExtract::NOverwriteMode::kAutoRename; + break; + default: + return E_FAIL; + } + } + } + if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename) + { + if (!AutoRenamePath(fullProcessedPath)) + { + UString message = UString(kCantAutoRename) + fullProcessedPath; + RINOK(_extractCallback2->MessageError(message)); + return E_FAIL; + } + } + else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) + { + UString existPath = fullProcessedPath; + if (!AutoRenamePath(existPath)) + { + UString message = kCantAutoRename + fullProcessedPath; + RINOK(_extractCallback2->MessageError(message)); + return E_FAIL; + } + if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) + { + UString message = UString(kCantRenameFile) + fullProcessedPath; + RINOK(_extractCallback2->MessageError(message)); + return E_FAIL; + } + } + else + if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) + { + UString message = UString(kCantDeleteOutputFile) + fullProcessedPath; + RINOK(_extractCallback2->MessageError(message)); + return S_OK; + // return E_FAIL; + } + } + } + if (!isAnti) + { + _outFileStreamSpec = new COutFileStream; + CMyComPtr outStreamLoc(_outFileStreamSpec); + if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) + { + // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) + { + UString message = L"can not open output file " + fullProcessedPath; + RINOK(_extractCallback2->MessageError(message)); + return S_OK; + } + } + if (_isSplit) + { + RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); + } + _outFileStream = outStreamLoc; + *outStream = outStreamLoc.Detach(); + } + _diskFilePath = fullProcessedPath; + } + else + { + *outStream = NULL; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) +{ + COM_TRY_BEGIN + _extractMode = false; + switch (askExtractMode) + { + case NArchive::NExtract::NAskMode::kExtract: + _extractMode = true; + }; + return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDirectory, + askExtractMode, _isSplit ? &_position: 0); + COM_TRY_END +} + +STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) +{ + COM_TRY_BEGIN + switch(operationResult) + { + case NArchive::NExtract::NOperationResult::kOK: + case NArchive::NExtract::NOperationResult::kUnSupportedMethod: + case NArchive::NExtract::NOperationResult::kCRCError: + case NArchive::NExtract::NOperationResult::kDataError: + break; + default: + _outFileStream.Release(); + return E_FAIL; + } + if (_outFileStream != NULL) + { + _outFileStreamSpec->SetTime( + (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL, + (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL, + (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault); + _curSize = _outFileStreamSpec->ProcessedSize; + RINOK(_outFileStreamSpec->Close()); + _outFileStream.Release(); + } + UnpackSize += _curSize; + if (_processedFileInfo.IsDirectory) + NumFolders++; + else + NumFiles++; + + if (_extractMode && _processedFileInfo.AttributesAreDefined) + NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes); + RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); + return S_OK; + COM_TRY_END +} + +/* +STDMETHODIMP CArchiveExtractCallback::GetInStream( + const wchar_t *name, ISequentialInStream **inStream) +{ + COM_TRY_BEGIN + CInFileStream *inFile = new CInFileStream; + CMyComPtr inStreamTemp = inFile; + if (!inFile->Open(_srcDirectoryPrefix + name)) + return ::GetLastError(); + *inStream = inStreamTemp.Detach(); + return S_OK; + COM_TRY_END +} +*/ + +STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) +{ + COM_TRY_BEGIN + if (!_cryptoGetTextPassword) + { + RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, + &_cryptoGetTextPassword)); + } + return _cryptoGetTextPassword->CryptoGetTextPassword(password); + COM_TRY_END +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h new file mode 100644 index 0000000..fd30d64 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h @@ -0,0 +1,139 @@ +// ArchiveExtractCallback.h + +#ifndef __ARCHIVEEXTRACTCALLBACK_H +#define __ARCHIVEEXTRACTCALLBACK_H + +#include "../../Archive/IArchive.h" +#include "IFileExtractCallback.h" + +#include "Common/MyString.h" +#include "Common/MyCom.h" + +#include "../../Common/FileStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../IPassword.h" + +#include "ExtractMode.h" + +class CArchiveExtractCallback: + public IArchiveExtractCallback, + // public IArchiveVolumeExtractCallback, + public ICryptoGetTextPassword, + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo) + // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback) + + // IProgress + STDMETHOD(SetTotal)(UInt64 size); + STDMETHOD(SetCompleted)(const UInt64 *completeValue); + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); + + // IExtractCallBack + STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode); + STDMETHOD(PrepareOperation)(Int32 askExtractMode); + STDMETHOD(SetOperationResult)(Int32 resultEOperationResult); + + // IArchiveVolumeExtractCallback + // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream); + + // ICryptoGetTextPassword + STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword); + +private: + CMyComPtr _archiveHandler; + CMyComPtr _extractCallback2; + CMyComPtr _compressProgress; + CMyComPtr _cryptoGetTextPassword; + UString _directoryPath; + NExtract::NPathMode::EEnum _pathMode; + NExtract::NOverwriteMode::EEnum _overwriteMode; + + UString _filePath; + UInt64 _position; + bool _isSplit; + + UString _diskFilePath; + + bool _extractMode; + + bool WriteModified; + bool WriteCreated; + bool WriteAccessed; + + bool _encrypted; + + struct CProcessedFileInfo + { + FILETIME CreationTime; + FILETIME LastWriteTime; + FILETIME LastAccessTime; + UInt32 Attributes; + + bool IsCreationTimeDefined; + bool IsLastWriteTimeDefined; + bool IsLastAccessTimeDefined; + + bool IsDirectory; + bool AttributesAreDefined; + } _processedFileInfo; + + UInt64 _curSize; + COutFileStream *_outFileStreamSpec; + CMyComPtr _outFileStream; + UStringVector _removePathParts; + + UString _itemDefaultName; + FILETIME _utcLastWriteTimeDefault; + UInt32 _attributesDefault; + bool _stdOutMode; + + void CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath); + HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); +public: + CArchiveExtractCallback(): + WriteModified(true), + WriteCreated(false), + WriteAccessed(false), + _multiArchives(false) + { + LocalProgressSpec = new CLocalProgress(); + _localProgress = LocalProgressSpec; + } + + CLocalProgress *LocalProgressSpec; + CMyComPtr _localProgress; + UInt64 _packTotal; + UInt64 _unpTotal; + + bool _multiArchives; + UInt64 NumFolders; + UInt64 NumFiles; + UInt64 UnpackSize; + + void InitForMulti(bool multiArchives, + NExtract::NPathMode::EEnum pathMode, + NExtract::NOverwriteMode::EEnum overwriteMode) + { + _multiArchives = multiArchives; NumFolders = NumFiles = UnpackSize = 0; + _pathMode = pathMode; + _overwriteMode = overwriteMode; + } + + void Init( + IInArchive *archiveHandler, + IFolderArchiveExtractCallback *extractCallback2, + bool stdOutMode, + const UString &directoryPath, + const UStringVector &removePathParts, + const UString &itemDefaultName, + const FILETIME &utcLastWriteTimeDefault, + UInt32 attributesDefault, + UInt64 packSize); + + UInt64 _numErrors; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.cpp new file mode 100644 index 0000000..2d50ede --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.cpp @@ -0,0 +1,46 @@ +// ArchiveName.cpp + +#include "StdAfx.h" + +#include "Windows/FileFind.h" +#include "Windows/FileDir.h" + +using namespace NWindows; + +UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName) +{ + UString resultName = L"Archive"; + if (fromPrev) + { + UString dirPrefix; + if (NFile::NDirectory::GetOnlyDirPrefix(srcName, dirPrefix)) + { + if (dirPrefix.Length() > 0) + if (dirPrefix[dirPrefix.Length() - 1] == '\\') + { + dirPrefix.Delete(dirPrefix.Length() - 1); + NFile::NFind::CFileInfoW fileInfo; + if (NFile::NFind::FindFile(dirPrefix, fileInfo)) + resultName = fileInfo.Name; + } + } + } + else + { + NFile::NFind::CFileInfoW fileInfo; + if (!NFile::NFind::FindFile(srcName, fileInfo)) + return resultName; + resultName = fileInfo.Name; + if (!fileInfo.IsDirectory() && !keepName) + { + int dotPos = resultName.ReverseFind('.'); + if (dotPos > 0) + { + UString archiveName2 = resultName.Left(dotPos); + if (archiveName2.ReverseFind('.') < 0) + resultName = archiveName2; + } + } + } + return resultName; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.h new file mode 100644 index 0000000..9513fb2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveName.h @@ -0,0 +1,10 @@ +// ArchiveName.h + +#ifndef __ARCHIVENAME_H +#define __ARCHIVENAME_H + +#include "Common/MyString.h" + +UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp new file mode 100644 index 0000000..2f0c41a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp @@ -0,0 +1,137 @@ +// ArchiveOpenCallback.cpp + +#include "StdAfx.h" + +#include "ArchiveOpenCallback.h" + +#include "Common/StringConvert.h" +#include "Common/ComTry.h" +#include "Windows/PropVariant.h" + +#include "../../Common/FileStreams.h" + +using namespace NWindows; + +STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes) +{ + COM_TRY_BEGIN + if (!Callback) + return S_OK; + return Callback->SetTotal(files, bytes); + COM_TRY_END +} + +STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes) +{ + COM_TRY_BEGIN + if (!Callback) + return S_OK; + return Callback->SetTotal(files, bytes); + COM_TRY_END +} + +STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant propVariant; + if (_subArchiveMode) + { + switch(propID) + { + case kpidName: + propVariant = _subArchiveName; + break; + } + propVariant.Detach(value); + return S_OK; + } + switch(propID) + { + case kpidName: + propVariant = _fileInfo.Name; + break; + case kpidIsFolder: + propVariant = _fileInfo.IsDirectory(); + break; + case kpidSize: + propVariant = _fileInfo.Size; + break; + case kpidAttributes: + propVariant = (UInt32)_fileInfo.Attributes; + break; + case kpidLastAccessTime: + propVariant = _fileInfo.LastAccessTime; + break; + case kpidCreationTime: + propVariant = _fileInfo.CreationTime; + break; + case kpidLastWriteTime: + propVariant = _fileInfo.LastWriteTime; + break; + } + propVariant.Detach(value); + return S_OK; + COM_TRY_END +} + +int COpenCallbackImp::FindName(const UString &name) +{ + for (int i = 0; i < FileNames.Size(); i++) + if (name.CompareNoCase(FileNames[i]) == 0) + return i; + return -1; +} + +struct CInFileStreamVol: public CInFileStream +{ + UString Name; + COpenCallbackImp *OpenCallbackImp; + CMyComPtr OpenCallbackRef; + ~CInFileStreamVol() + { + int index = OpenCallbackImp->FindName(Name); + if (index >= 0) + OpenCallbackImp->FileNames.Delete(index); + } +}; + +STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream) +{ + COM_TRY_BEGIN + if (_subArchiveMode) + return S_FALSE; + if (Callback) + { + RINOK(Callback->CheckBreak()); + } + *inStream = NULL; + UString fullPath = _folderPrefix + name; + if (!NFile::NFind::FindFile(fullPath, _fileInfo)) + return S_FALSE; + if (_fileInfo.IsDirectory()) + return S_FALSE; + CInFileStreamVol *inFile = new CInFileStreamVol; + CMyComPtr inStreamTemp = inFile; + if (!inFile->Open(fullPath)) + return ::GetLastError(); + *inStream = inStreamTemp.Detach(); + inFile->Name = name; + inFile->OpenCallbackImp = this; + inFile->OpenCallbackRef = this; + FileNames.Add(name); + TotalSize += _fileInfo.Size; + return S_OK; + COM_TRY_END +} + +#ifndef _NO_CRYPTO +STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password) +{ + COM_TRY_BEGIN + if (!Callback) + return E_NOTIMPL; + return Callback->CryptoGetTextPassword(password); + COM_TRY_END +} +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h new file mode 100644 index 0000000..12b2b32 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h @@ -0,0 +1,93 @@ +// ArchiveOpenCallback.h + +#ifndef __ARCHIVE_OPEN_CALLBACK_H +#define __ARCHIVE_OPEN_CALLBACK_H + +#include "Common/MyString.h" +#include "Common/MyCom.h" +#include "Windows/FileFind.h" + +#ifndef _NO_CRYPTO +#include "../../IPassword.h" +#endif +#include "../../Archive/IArchive.h" + +struct IOpenCallbackUI +{ + virtual HRESULT CheckBreak() = 0; + virtual HRESULT SetTotal(const UInt64 *files, const UInt64 *bytes) = 0; + virtual HRESULT SetCompleted(const UInt64 *files, const UInt64 *bytes) = 0; + #ifndef _NO_CRYPTO + virtual HRESULT CryptoGetTextPassword(BSTR *password) = 0; + virtual HRESULT GetPasswordIfAny(UString &password) = 0; + virtual bool WasPasswordAsked() = 0; + virtual void ClearPasswordWasAskedFlag() = 0; + #endif +}; + +class COpenCallbackImp: + public IArchiveOpenCallback, + public IArchiveOpenVolumeCallback, + public IArchiveOpenSetSubArchiveName, + #ifndef _NO_CRYPTO + public ICryptoGetTextPassword, + #endif + public CMyUnknownImp +{ +public: + #ifndef _NO_CRYPTO + MY_UNKNOWN_IMP3( + IArchiveOpenVolumeCallback, + ICryptoGetTextPassword, + IArchiveOpenSetSubArchiveName + ) + #else + MY_UNKNOWN_IMP2( + IArchiveOpenVolumeCallback, + IArchiveOpenSetSubArchiveName + ) + #endif + + STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); + STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); + + // IArchiveOpenVolumeCallback + STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value); + STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream); + + #ifndef _NO_CRYPTO + // ICryptoGetTextPassword + STDMETHOD(CryptoGetTextPassword)(BSTR *password); + #endif + + STDMETHOD(SetSubArchiveName(const wchar_t *name)) + { + _subArchiveMode = true; + _subArchiveName = name; + return S_OK; + } + +private: + UString _folderPrefix; + NWindows::NFile::NFind::CFileInfoW _fileInfo; + bool _subArchiveMode; + UString _subArchiveName; +public: + UStringVector FileNames; + IOpenCallbackUI *Callback; + UInt64 TotalSize; + + COpenCallbackImp(): Callback(NULL) {} + void Init(const UString &folderPrefix, const UString &fileName) + { + _folderPrefix = folderPrefix; + if (!NWindows::NFile::NFind::FindFile(_folderPrefix + fileName, _fileInfo)) + throw 1; + FileNames.Clear(); + _subArchiveMode = false; + TotalSize = 0; + } + int FindName(const UString &name); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.cpp new file mode 100644 index 0000000..8ee7c04 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.cpp @@ -0,0 +1,26 @@ +// DefaultName.cpp + +#include "StdAfx.h" + +#include "DefaultName.h" + +static const wchar_t *kEmptyFileAlias = L"[Content]"; + +UString GetDefaultName2(const UString &fileName, + const UString &extension, const UString &addSubExtension) +{ + int extLength = extension.Length(); + int fileNameLength = fileName.Length(); + if (fileNameLength > extLength + 1) + { + int dotPos = fileNameLength - (extLength + 1); + if (fileName[dotPos] == '.') + if (extension.CompareNoCase(fileName.Mid(dotPos + 1)) == 0) + return fileName.Left(dotPos) + addSubExtension; + } + int dotPos = fileName.ReverseFind(L'.'); + if (dotPos > 0) + return fileName.Left(dotPos) + addSubExtension; + return kEmptyFileAlias; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.h new file mode 100644 index 0000000..a702cb0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DefaultName.h @@ -0,0 +1,11 @@ +// DefaultName.h + +#ifndef __DEFAULTNAME_H +#define __DEFAULTNAME_H + +#include "Common/MyString.h" + +UString GetDefaultName2(const UString &fileName, + const UString &extension, const UString &addSubExtension); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DirItem.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DirItem.h new file mode 100644 index 0000000..89bd4cd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/DirItem.h @@ -0,0 +1,34 @@ +// DirItem.h + +#ifndef __DIR_ITEM_H +#define __DIR_ITEM_H + +#include "Common/MyString.h" +#include "Common/Types.h" + +struct CDirItem +{ + UInt32 Attributes; + FILETIME CreationTime; + FILETIME LastAccessTime; + FILETIME LastWriteTime; + UInt64 Size; + UString Name; + UString FullPath; + bool IsDirectory() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } +}; + +struct CArchiveItem +{ + bool IsDirectory; + // DWORD Attributes; + // NWindows::NCOM::CPropVariant LastWriteTime; + FILETIME LastWriteTime; + bool SizeIsDefined; + UInt64 Size; + UString Name; + bool Censored; + int IndexInServer; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp new file mode 100644 index 0000000..454092e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp @@ -0,0 +1,281 @@ +// EnumDirItems.cpp + +#include "StdAfx.h" + +#include "Common/StringConvert.h" +#include "Common/Wildcard.h" +#include "Common/MyCom.h" + +#include "EnumDirItems.h" + +using namespace NWindows; +using namespace NFile; +using namespace NName; + +void AddDirFileInfo( + const UString &prefix, // prefix for logical path + const UString &fullPathName, // path on disk: can be relative to some basePrefix + const NFind::CFileInfoW &fileInfo, + CObjectVector &dirItems) +{ + CDirItem item; + item.Attributes = fileInfo.Attributes; + item.Size = fileInfo.Size; + item.CreationTime = fileInfo.CreationTime; + item.LastAccessTime = fileInfo.LastAccessTime; + item.LastWriteTime = fileInfo.LastWriteTime; + item.Name = prefix + fileInfo.Name; + item.FullPath = fullPathName; + dirItems.Add(item); +} + +static void EnumerateDirectory( + const UString &baseFolderPrefix, // base (disk) prefix for scanning + const UString &directory, // additional disk prefix starting from baseFolderPrefix + const UString &prefix, // logical prefix + CObjectVector &dirItems, + UStringVector &errorPaths, + CRecordVector &errorCodes) +{ + NFind::CEnumeratorW enumerator(baseFolderPrefix + directory + wchar_t(kAnyStringWildcard)); + for (;;) + { + NFind::CFileInfoW fileInfo; + bool found; + if (!enumerator.Next(fileInfo, found)) + { + errorCodes.Add(::GetLastError()); + errorPaths.Add(baseFolderPrefix + directory); + return; + } + if (!found) + break; + AddDirFileInfo(prefix, directory + fileInfo.Name, fileInfo, dirItems); + if (fileInfo.IsDirectory()) + { + EnumerateDirectory(baseFolderPrefix, directory + fileInfo.Name + wchar_t(kDirDelimiter), + prefix + fileInfo.Name + wchar_t(kDirDelimiter), dirItems, errorPaths, errorCodes); + } + } +} + +void EnumerateDirItems( + const UString &baseFolderPrefix, // base (disk) prefix for scanning + const UStringVector &fileNames, // names relative to baseFolderPrefix + const UString &archiveNamePrefix, + CObjectVector &dirItems, + UStringVector &errorPaths, + CRecordVector &errorCodes) +{ + for(int i = 0; i < fileNames.Size(); i++) + { + const UString &fileName = fileNames[i]; + NFind::CFileInfoW fileInfo; + if (!NFind::FindFile(baseFolderPrefix + fileName, fileInfo)) + { + errorCodes.Add(::GetLastError()); + errorPaths.Add(baseFolderPrefix + fileName); + continue; + } + AddDirFileInfo(archiveNamePrefix, fileName, fileInfo, dirItems); + if (fileInfo.IsDirectory()) + { + EnumerateDirectory(baseFolderPrefix, fileName + wchar_t(kDirDelimiter), + archiveNamePrefix + fileInfo.Name + wchar_t(kDirDelimiter), + dirItems, errorPaths, errorCodes); + } + } +} + +static HRESULT EnumerateDirItems( + const NWildcard::CCensorNode &curNode, + const UString &diskPrefix, // full disk path prefix + const UString &archivePrefix, // prefix from root + const UStringVector &addArchivePrefix, // prefix from curNode + CObjectVector &dirItems, + bool enterToSubFolders, + IEnumDirItemCallback *callback, + UStringVector &errorPaths, + CRecordVector &errorCodes) +{ + if (!enterToSubFolders) + if (curNode.NeedCheckSubDirs()) + enterToSubFolders = true; + if (callback) + RINOK(callback->CheckBreak()); + + // try direct_names case at first + if (addArchivePrefix.IsEmpty() && !enterToSubFolders) + { + // check that all names are direct + int i; + for (i = 0; i < curNode.IncludeItems.Size(); i++) + { + const NWildcard::CItem &item = curNode.IncludeItems[i]; + if (item.Recursive || item.PathParts.Size() != 1) + break; + const UString &name = item.PathParts.Front(); + if (name.IsEmpty() || DoesNameContainWildCard(name)) + break; + } + if (i == curNode.IncludeItems.Size()) + { + // all names are direct (no wildcards) + // so we don't need file_system's dir enumerator + CRecordVector needEnterVector; + for (i = 0; i < curNode.IncludeItems.Size(); i++) + { + const NWildcard::CItem &item = curNode.IncludeItems[i]; + const UString &name = item.PathParts.Front(); + const UString fullPath = diskPrefix + name; + NFind::CFileInfoW fileInfo; + if (!NFind::FindFile(fullPath, fileInfo)) + { + errorCodes.Add(::GetLastError()); + errorPaths.Add(fullPath); + continue; + } + bool isDir = fileInfo.IsDirectory(); + if (isDir && !item.ForDir || !isDir && !item.ForFile) + { + errorCodes.Add((DWORD)E_FAIL); + errorPaths.Add(fullPath); + continue; + } + const UString realName = fileInfo.Name; + const UString realDiskPath = diskPrefix + realName; + { + UStringVector pathParts; + pathParts.Add(fileInfo.Name); + if (curNode.CheckPathToRoot(false, pathParts, !isDir)) + continue; + } + AddDirFileInfo(archivePrefix, realDiskPath, fileInfo, dirItems); + if (!isDir) + continue; + + UStringVector addArchivePrefixNew; + const NWildcard::CCensorNode *nextNode = 0; + int index = curNode.FindSubNode(name); + if (index >= 0) + { + for (int t = needEnterVector.Size(); t <= index; t++) + needEnterVector.Add(true); + needEnterVector[index] = false; + nextNode = &curNode.SubNodes[index]; + } + else + { + nextNode = &curNode; + addArchivePrefixNew.Add(name); // don't change it to realName. It's for shortnames support + } + RINOK(EnumerateDirItems(*nextNode, + realDiskPath + wchar_t(kDirDelimiter), + archivePrefix + realName + wchar_t(kDirDelimiter), + addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes)); + } + for (i = 0; i < curNode.SubNodes.Size(); i++) + { + if (i < needEnterVector.Size()) + if (!needEnterVector[i]) + continue; + const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; + const UString fullPath = diskPrefix + nextNode.Name; + NFind::CFileInfoW fileInfo; + if (!NFind::FindFile(fullPath, fileInfo)) + { + if (!nextNode.AreThereIncludeItems()) + continue; + errorCodes.Add(::GetLastError()); + errorPaths.Add(fullPath); + continue; + } + if (!fileInfo.IsDirectory()) + { + errorCodes.Add((DWORD)E_FAIL); + errorPaths.Add(fullPath); + continue; + } + RINOK(EnumerateDirItems(nextNode, + diskPrefix + fileInfo.Name + wchar_t(kDirDelimiter), + archivePrefix + fileInfo.Name + wchar_t(kDirDelimiter), + UStringVector(), dirItems, false, callback, errorPaths, errorCodes)); + } + return S_OK; + } + } + + + NFind::CEnumeratorW enumerator(diskPrefix + wchar_t(kAnyStringWildcard)); + for (;;) + { + NFind::CFileInfoW fileInfo; + bool found; + if (!enumerator.Next(fileInfo, found)) + { + errorCodes.Add(::GetLastError()); + errorPaths.Add(diskPrefix); + break; + } + if (!found) + break; + + if (callback) + RINOK(callback->CheckBreak()); + const UString &name = fileInfo.Name; + bool enterToSubFolders2 = enterToSubFolders; + UStringVector addArchivePrefixNew = addArchivePrefix; + addArchivePrefixNew.Add(name); + { + UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); + if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fileInfo.IsDirectory())) + continue; + } + if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fileInfo.IsDirectory())) + { + AddDirFileInfo(archivePrefix, diskPrefix + name, fileInfo, dirItems); + if (fileInfo.IsDirectory()) + enterToSubFolders2 = true; + } + if (!fileInfo.IsDirectory()) + continue; + + const NWildcard::CCensorNode *nextNode = 0; + if (addArchivePrefix.IsEmpty()) + { + int index = curNode.FindSubNode(name); + if (index >= 0) + nextNode = &curNode.SubNodes[index]; + } + if (!enterToSubFolders2 && nextNode == 0) + continue; + + addArchivePrefixNew = addArchivePrefix; + if (nextNode == 0) + { + nextNode = &curNode; + addArchivePrefixNew.Add(name); + } + RINOK(EnumerateDirItems(*nextNode, + diskPrefix + name + wchar_t(kDirDelimiter), + archivePrefix + name + wchar_t(kDirDelimiter), + addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes)); + } + return S_OK; +} + +HRESULT EnumerateItems( + const NWildcard::CCensor &censor, + CObjectVector &dirItems, + IEnumDirItemCallback *callback, + UStringVector &errorPaths, + CRecordVector &errorCodes) +{ + for (int i = 0; i < censor.Pairs.Size(); i++) + { + const NWildcard::CPair &pair = censor.Pairs[i]; + RINOK(EnumerateDirItems(pair.Head, pair.Prefix, L"", UStringVector(), dirItems, false, + callback, errorPaths, errorCodes)); + } + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.h new file mode 100644 index 0000000..8d5495a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/EnumDirItems.h @@ -0,0 +1,39 @@ +// EnumDirItems.h + +#ifndef __ENUM_DIR_ITEMS_H +#define __ENUM_DIR_ITEMS_H + +#include "Common/Wildcard.h" +#include "DirItem.h" + +#include "Windows/FileFind.h" + +void AddDirFileInfo( + const UString &prefix, + const UString &fullPathName, + const NWindows::NFile::NFind::CFileInfoW &fileInfo, + CObjectVector &dirItems); + + +void EnumerateDirItems( + const UString &baseFolderPrefix, + const UStringVector &fileNames, + const UString &archiveNamePrefix, + CObjectVector &dirItems, + UStringVector &errorPaths, + CRecordVector &errorCodes); + +struct IEnumDirItemCallback +{ + virtual HRESULT CheckBreak() { return S_OK; } +}; + + +HRESULT EnumerateItems( + const NWildcard::CCensor &censor, + CObjectVector &dirItems, + IEnumDirItemCallback *callback, + UStringVector &errorPaths, + CRecordVector &errorCodes); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExitCode.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExitCode.h new file mode 100644 index 0000000..0aac369 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExitCode.h @@ -0,0 +1,27 @@ +// ExitCode.h + +#ifndef __EXIT_CODE_H +#define __EXIT_CODE_H + +namespace NExitCode { + +enum EEnum { + + kSuccess = 0, // Successful operation + kWarning = 1, // Non fatal error(s) occurred + kFatalError = 2, // A fatal error occurred + // kCRCError = 3, // A CRC error occurred when unpacking + // kLockedArchive = 4, // Attempt to modify an archive previously locked + // kWriteError = 5, // Write to disk error + // kOpenError = 6, // Open file error + kUserError = 7, // Command line option error + kMemoryError = 8, // Not enough memory for operation + // kCreateFileError = 9, // Create file error + + kUserBreak = 255 // User stopped the process + +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.cpp new file mode 100644 index 0000000..0e56a08 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.cpp @@ -0,0 +1,187 @@ +// Extract.cpp + +#include "StdAfx.h" + +#include "Extract.h" + +#include "Windows/Defs.h" +#include "Windows/FileDir.h" + +#include "OpenArchive.h" +#include "SetProperties.h" + +using namespace NWindows; + +HRESULT DecompressArchive( + IInArchive *archive, + UInt64 packSize, + const UString &defaultName, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &options, + IExtractCallbackUI *callback, + CArchiveExtractCallback *extractCallbackSpec, + UString &errorMessage) +{ + CRecordVector realIndices; + UInt32 numItems; + RINOK(archive->GetNumberOfItems(&numItems)); + + for(UInt32 i = 0; i < numItems; i++) + { + UString filePath; + RINOK(GetArchiveItemPath(archive, i, options.DefaultItemName, filePath)); + bool isFolder; + RINOK(IsArchiveItemFolder(archive, i, isFolder)); + if (!wildcardCensor.CheckPath(filePath, !isFolder)) + continue; + realIndices.Add(i); + } + if (realIndices.Size() == 0) + { + callback->ThereAreNoFiles(); + return S_OK; + } + + UStringVector removePathParts; + + UString outDir = options.OutputDir; + outDir.Replace(L"*", defaultName); + if(!outDir.IsEmpty()) + if(!NFile::NDirectory::CreateComplexDirectory(outDir)) + { + HRESULT res = ::GetLastError(); + if (res == S_OK) + res = E_FAIL; + errorMessage = ((UString)L"Can not create output directory ") + outDir; + return res; + } + + extractCallbackSpec->Init( + archive, + callback, + options.StdOutMode, + outDir, + removePathParts, + options.DefaultItemName, + options.ArchiveFileInfo.LastWriteTime, + options.ArchiveFileInfo.Attributes, + packSize); + + #ifdef COMPRESS_MT + RINOK(SetProperties(archive, options.Properties)); + #endif + + HRESULT result = archive->Extract(&realIndices.Front(), + realIndices.Size(), options.TestMode? 1: 0, extractCallbackSpec); + + return callback->ExtractResult(result); +} + +HRESULT DecompressArchives( + CCodecs *codecs, + UStringVector &archivePaths, UStringVector &archivePathsFull, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &optionsSpec, + IOpenCallbackUI *openCallback, + IExtractCallbackUI *extractCallback, + UString &errorMessage, + CDecompressStat &stat) +{ + stat.Clear(); + CExtractOptions options = optionsSpec; + int i; + UInt64 totalPackSize = 0; + CRecordVector archiveSizes; + for (i = 0; i < archivePaths.Size(); i++) + { + const UString &archivePath = archivePaths[i]; + NFile::NFind::CFileInfoW archiveFileInfo; + if (!NFile::NFind::FindFile(archivePath, archiveFileInfo)) + throw "there is no such archive"; + if (archiveFileInfo.IsDirectory()) + throw "can't decompress folder"; + archiveSizes.Add(archiveFileInfo.Size); + totalPackSize += archiveFileInfo.Size; + } + CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; + CMyComPtr ec(extractCallbackSpec); + bool multi = (archivePaths.Size() > 1); + extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode); + if (multi) + { + RINOK(extractCallback->SetTotal(totalPackSize)); + } + for (i = 0; i < archivePaths.Size(); i++) + { + const UString &archivePath = archivePaths[i]; + NFile::NFind::CFileInfoW archiveFileInfo; + if (!NFile::NFind::FindFile(archivePath, archiveFileInfo)) + throw "there is no such archive"; + + if (archiveFileInfo.IsDirectory()) + throw "there is no such archive"; + + options.ArchiveFileInfo = archiveFileInfo; + + #ifndef _NO_CRYPTO + openCallback->ClearPasswordWasAskedFlag(); + #endif + + RINOK(extractCallback->BeforeOpen(archivePath)); + CArchiveLink archiveLink; + HRESULT result = MyOpenArchive(codecs, archivePath, archiveLink, openCallback); + + bool crypted = false; + #ifndef _NO_CRYPTO + crypted = openCallback->WasPasswordAsked(); + #endif + + RINOK(extractCallback->OpenResult(archivePath, result, crypted)); + if (result != S_OK) + continue; + + for (int v = 0; v < archiveLink.VolumePaths.Size(); v++) + { + int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]); + if (index >= 0 && index > i) + { + archivePaths.Delete(index); + archivePathsFull.Delete(index); + totalPackSize -= archiveSizes[index]; + archiveSizes.Delete(index); + } + } + if (archiveLink.VolumePaths.Size() != 0) + { + totalPackSize += archiveLink.VolumesSize; + RINOK(extractCallback->SetTotal(totalPackSize)); + } + + #ifndef _NO_CRYPTO + UString password; + RINOK(openCallback->GetPasswordIfAny(password)); + if (!password.IsEmpty()) + { + RINOK(extractCallback->SetPassword(password)); + } + #endif + + options.DefaultItemName = archiveLink.GetDefaultItemName(); + RINOK(DecompressArchive( + archiveLink.GetArchive(), + archiveFileInfo.Size + archiveLink.VolumesSize, + archiveLink.GetDefaultItemName(), + wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage)); + extractCallbackSpec->LocalProgressSpec->InSize += archiveFileInfo.Size + + archiveLink.VolumesSize; + extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize; + if (!errorMessage.IsEmpty()) + return E_FAIL; + } + stat.NumFolders = extractCallbackSpec->NumFolders; + stat.NumFiles = extractCallbackSpec->NumFiles; + stat.UnpackSize = extractCallbackSpec->UnpackSize; + stat.NumArchives = archivePaths.Size(); + stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.h new file mode 100644 index 0000000..e7add12 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Extract.h @@ -0,0 +1,77 @@ +// Extract.h + +#ifndef __EXTRACT_H +#define __EXTRACT_H + +#include "Common/Wildcard.h" +#include "Windows/FileFind.h" + +#include "../../Archive/IArchive.h" + +#include "ArchiveExtractCallback.h" +#include "ArchiveOpenCallback.h" +#include "ExtractMode.h" +#include "Property.h" + +#include "../Common/LoadCodecs.h" + +class CExtractOptions +{ +public: + bool StdOutMode; + bool TestMode; + NExtract::NPathMode::EEnum PathMode; + + UString OutputDir; + bool YesToAll; + UString DefaultItemName; + NWindows::NFile::NFind::CFileInfoW ArchiveFileInfo; + + // bool ShowDialog; + // bool PasswordEnabled; + // UString Password; + #ifdef COMPRESS_MT + CObjectVector Properties; + #endif + + NExtract::NOverwriteMode::EEnum OverwriteMode; + + #ifdef EXTERNAL_CODECS + CCodecs *Codecs; + #endif + + CExtractOptions(): + StdOutMode(false), + YesToAll(false), + TestMode(false), + PathMode(NExtract::NPathMode::kFullPathnames), + OverwriteMode(NExtract::NOverwriteMode::kAskBefore) + {} + + /* + bool FullPathMode() const { return (ExtractMode == NExtractMode::kTest) || + (ExtractMode == NExtractMode::kFullPath); } + */ +}; + +struct CDecompressStat +{ + UInt64 NumArchives; + UInt64 UnpackSize; + UInt64 PackSize; + UInt64 NumFolders; + UInt64 NumFiles; + void Clear() { NumArchives = PackSize = UnpackSize = NumFolders = NumFiles = 0; } +}; + +HRESULT DecompressArchives( + CCodecs *codecs, + UStringVector &archivePaths, UStringVector &archivePathsFull, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &options, + IOpenCallbackUI *openCallback, + IExtractCallbackUI *extractCallback, + UString &errorMessage, + CDecompressStat &stat); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractMode.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractMode.h new file mode 100644 index 0000000..96b5a8c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractMode.h @@ -0,0 +1,31 @@ +// ExtractMode.h + +#ifndef __EXTRACT_MODE_H +#define __EXTRACT_MODE_H + +namespace NExtract { + + namespace NPathMode + { + enum EEnum + { + kFullPathnames, + kCurrentPathnames, + kNoPathnames + }; + } + + namespace NOverwriteMode + { + enum EEnum + { + kAskBefore, + kWithoutPrompt, + kSkipExisting, + kAutoRename, + kAutoRenameExisting + }; + } +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp new file mode 100644 index 0000000..fa796ca --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp @@ -0,0 +1,96 @@ +// ExtractingFilePath.cpp + +#include "StdAfx.h" +#include "ExtractingFilePath.h" + +static UString ReplaceIncorrectChars(const UString &s) +{ + #ifdef _WIN32 + UString res; + for (int i = 0; i < s.Length(); i++) + { + wchar_t c = s[i]; + if (c < 0x20 || c == '*' || c == '?' || c == '<' || c == '>' || c == '|' || c == ':' || c == '"') + c = '_'; + res += c; + } + return res; + #else + return s; + #endif +} + +#ifdef _WIN32 +static const wchar_t *g_ReservedNames[] = +{ + L"CON", L"PRN", L"AUX", L"NUL" +}; + +static bool CheckTail(const UString &name, int len) +{ + int dotPos = name.Find(L'.'); + if (dotPos < 0) + dotPos = name.Length(); + UString s = name.Left(dotPos); + s.TrimRight(); + return (s.Length() != len); +} + +static bool CheckNameNum(const UString &name, const wchar_t *reservedName) +{ + int len = MyStringLen(reservedName); + if (name.Length() <= len) + return true; + if (name.Left(len).CompareNoCase(reservedName) != 0) + return true; + wchar_t c = name[len]; + if (c < L'0' || c > L'9') + return true; + return CheckTail(name, len + 1); +} + +static bool IsSupportedName(const UString &name) +{ + for (int i = 0; i < sizeof(g_ReservedNames) / sizeof(g_ReservedNames[0]); i++) + { + const wchar_t *reservedName = g_ReservedNames[i]; + int len = MyStringLen(reservedName); + if (name.Length() < len) + continue; + if (name.Left(len).CompareNoCase(reservedName) != 0) + continue; + if (!CheckTail(name, len)) + return false; + } + if (!CheckNameNum(name, L"COM")) + return false; + return CheckNameNum(name, L"LPT"); +} +#endif + +static UString GetCorrectFileName(const UString &path) +{ + if (path == L".." || path == L".") + return UString(); + return ReplaceIncorrectChars(path); +} + +void MakeCorrectPath(UStringVector &pathParts) +{ + for (int i = 0; i < pathParts.Size();) + { + UString &s = pathParts[i]; + s = GetCorrectFileName(s); + if (s.IsEmpty()) + pathParts.Delete(i); + else + { + #ifdef _WIN32 + if (!IsSupportedName(s)) + s = (UString)L"_" + s; + #endif + i++; + } + } +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h new file mode 100644 index 0000000..a86a6a9 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h @@ -0,0 +1,10 @@ +// ExtractingFilePath.h + +#ifndef __EXTRACTINGFILEPATH_H +#define __EXTRACTINGFILEPATH_H + +#include "Common/MyString.h" + +void MakeCorrectPath(UStringVector &pathParts); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h new file mode 100644 index 0000000..284e9cb --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h @@ -0,0 +1,43 @@ +// IFileExtractCallback.h + +#ifndef __IFILEEXTRACTCALLBACK_H +#define __IFILEEXTRACTCALLBACK_H + +#include "Common/MyString.h" +#include "../../IDecl.h" + +namespace NOverwriteAnswer +{ + enum EEnum + { + kYes, + kYesToAll, + kNo, + kNoToAll, + kAutoRename, + kCancel + }; +} + +DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07) +{ +public: + STDMETHOD(AskOverwrite)( + const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, + const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, + Int32 *answer) PURE; + STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE; + STDMETHOD(MessageError)(const wchar_t *message) PURE; + STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE; +}; + +struct IExtractCallbackUI: IFolderArchiveExtractCallback +{ + virtual HRESULT BeforeOpen(const wchar_t *name) = 0; + virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0; + virtual HRESULT ThereAreNoFiles() = 0; + virtual HRESULT ExtractResult(HRESULT result) = 0; + virtual HRESULT SetPassword(const UString &password) = 0; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp new file mode 100644 index 0000000..087340a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp @@ -0,0 +1,644 @@ +// LoadCodecs.cpp + +#include "StdAfx.h" + +#include "LoadCodecs.h" + +#include "../../../Common/MyCom.h" +#ifdef NEW_FOLDER_INTERFACE +#include "../../../Common/StringToInt.h" +#endif +#include "../../../Windows/PropVariant.h" + +#include "../../ICoder.h" +#include "../../Common/RegisterArc.h" + +#ifdef EXTERNAL_CODECS +#include "../../../Windows/FileFind.h" +#include "../../../Windows/DLL.h" +#ifdef NEW_FOLDER_INTERFACE +#include "../../../Windows/ResourceString.h" +static const UINT kIconTypesResId = 100; +#endif + +#ifdef _WIN32 +#include "Windows/Registry.h" +#endif + +using namespace NWindows; +using namespace NFile; + +#ifdef _WIN32 +extern HINSTANCE g_hInstance; +#endif + +static CSysString GetLibraryFolderPrefix() +{ + #ifdef _WIN32 + TCHAR fullPath[MAX_PATH + 1]; + ::GetModuleFileName(g_hInstance, fullPath, MAX_PATH); + CSysString path = fullPath; + int pos = path.ReverseFind(TEXT(CHAR_PATH_SEPARATOR)); + return path.Left(pos + 1); + #else + return CSysString(); // FIX IT + #endif +} + +#define kCodecsFolderName TEXT("Codecs") +#define kFormatsFolderName TEXT("Formats") +static TCHAR *kMainDll = TEXT("7z.dll"); + +#ifdef _WIN32 +static LPCTSTR kRegistryPath = TEXT("Software\\7-zip"); +static LPCTSTR kProgramPathValue = TEXT("Path"); +static bool ReadPathFromRegistry(HKEY baseKey, CSysString &path) +{ + NRegistry::CKey key; + if(key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS) + if (key.QueryValue(kProgramPathValue, path) == ERROR_SUCCESS) + { + NName::NormalizeDirPathPrefix(path); + return true; + } + return false; +} + +#endif + +CSysString GetBaseFolderPrefixFromRegistry() +{ + CSysString moduleFolderPrefix = GetLibraryFolderPrefix(); + NFind::CFileInfo fileInfo; + if (NFind::FindFile(moduleFolderPrefix + kMainDll, fileInfo)) + if (!fileInfo.IsDirectory()) + return moduleFolderPrefix; + if (NFind::FindFile(moduleFolderPrefix + kCodecsFolderName, fileInfo)) + if (fileInfo.IsDirectory()) + return moduleFolderPrefix; + if (NFind::FindFile(moduleFolderPrefix + kFormatsFolderName, fileInfo)) + if (fileInfo.IsDirectory()) + return moduleFolderPrefix; + #ifdef _WIN32 + CSysString path; + if (ReadPathFromRegistry(HKEY_CURRENT_USER, path)) + return path; + if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, path)) + return path; + #endif + return moduleFolderPrefix; +} + +typedef UInt32 (WINAPI *GetNumberOfMethodsFunc)(UInt32 *numMethods); +typedef UInt32 (WINAPI *GetNumberOfFormatsFunc)(UInt32 *numFormats); +typedef UInt32 (WINAPI *GetHandlerPropertyFunc)(PROPID propID, PROPVARIANT *value); +typedef UInt32 (WINAPI *GetHandlerPropertyFunc2)(UInt32 index, PROPID propID, PROPVARIANT *value); +typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *iid, void **outObject); +typedef UInt32 (WINAPI *SetLargePageModeFunc)(); + + +static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 index, + PROPID propId, CLSID &clsId, bool &isAssigned) +{ + NWindows::NCOM::CPropVariant prop; + isAssigned = false; + RINOK(getMethodProperty(index, propId, &prop)); + if (prop.vt == VT_BSTR) + { + isAssigned = true; + clsId = *(const GUID *)prop.bstrVal; + } + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; +} + +HRESULT CCodecs::LoadCodecs() +{ + CCodecLib &lib = Libs.Back(); + lib.GetMethodProperty = (GetMethodPropertyFunc)lib.Lib.GetProcAddress("GetMethodProperty"); + if (lib.GetMethodProperty == NULL) + return S_OK; + + UInt32 numMethods = 1; + GetNumberOfMethodsFunc getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)lib.Lib.GetProcAddress("GetNumberOfMethods"); + if (getNumberOfMethodsFunc != NULL) + { + RINOK(getNumberOfMethodsFunc(&numMethods)); + } + + for(UInt32 i = 0; i < numMethods; i++) + { + CDllCodecInfo info; + info.LibIndex = Libs.Size() - 1; + info.CodecIndex = i; + + RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned)); + RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned)); + + Codecs.Add(info); + } + return S_OK; +} + +static HRESULT ReadProp( + GetHandlerPropertyFunc getProp, + GetHandlerPropertyFunc2 getProp2, + UInt32 index, PROPID propID, NCOM::CPropVariant &prop) +{ + if (getProp2) + return getProp2(index, propID, &prop);; + return getProp(propID, &prop); +} + +static HRESULT ReadBoolProp( + GetHandlerPropertyFunc getProp, + GetHandlerPropertyFunc2 getProp2, + UInt32 index, PROPID propID, bool &res) +{ + NCOM::CPropVariant prop; + RINOK(ReadProp(getProp, getProp2, index, propID, prop)); + if (prop.vt == VT_BOOL) + res = VARIANT_BOOLToBool(prop.boolVal); + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; +} + +static HRESULT ReadStringProp( + GetHandlerPropertyFunc getProp, + GetHandlerPropertyFunc2 getProp2, + UInt32 index, PROPID propID, UString &res) +{ + NCOM::CPropVariant prop; + RINOK(ReadProp(getProp, getProp2, index, propID, prop)); + if (prop.vt == VT_BSTR) + res = prop.bstrVal; + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; +} + +#endif + +static const unsigned int kNumArcsMax = 32; +static unsigned int g_NumArcs = 0; +static const CArcInfo *g_Arcs[kNumArcsMax]; +void RegisterArc(const CArcInfo *arcInfo) +{ + if (g_NumArcs < kNumArcsMax) + g_Arcs[g_NumArcs++] = arcInfo; +} + +static void SplitString(const UString &srcString, UStringVector &destStrings) +{ + destStrings.Clear(); + UString s; + int len = srcString.Length(); + if (len == 0) + return; + for (int i = 0; i < len; i++) + { + wchar_t c = srcString[i]; + if (c == L' ') + { + if (!s.IsEmpty()) + { + destStrings.Add(s); + s.Empty(); + } + } + else + s += c; + } + if (!s.IsEmpty()) + destStrings.Add(s); +} + +void CArcInfoEx::AddExts(const wchar_t* ext, const wchar_t* addExt) +{ + UStringVector exts, addExts; + SplitString(ext, exts); + if (addExt != 0) + SplitString(addExt, addExts); + for (int i = 0; i < exts.Size(); i++) + { + CArcExtInfo extInfo; + extInfo.Ext = exts[i]; + if (i < addExts.Size()) + { + extInfo.AddExt = addExts[i]; + if (extInfo.AddExt == L"*") + extInfo.AddExt.Empty(); + } + Exts.Add(extInfo); + } +} + +#ifdef EXTERNAL_CODECS + +HRESULT CCodecs::LoadFormats() +{ + const NDLL::CLibrary &lib = Libs.Back().Lib; + GetHandlerPropertyFunc getProp = 0; + GetHandlerPropertyFunc2 getProp2 = (GetHandlerPropertyFunc2) + lib.GetProcAddress("GetHandlerProperty2"); + if (getProp2 == NULL) + { + getProp = (GetHandlerPropertyFunc) + lib.GetProcAddress("GetHandlerProperty"); + if (getProp == NULL) + return S_OK; + } + + UInt32 numFormats = 1; + GetNumberOfFormatsFunc getNumberOfFormats = (GetNumberOfFormatsFunc) + lib.GetProcAddress("GetNumberOfFormats"); + if (getNumberOfFormats != NULL) + { + RINOK(getNumberOfFormats(&numFormats)); + } + if (getProp2 == NULL) + numFormats = 1; + + for(UInt32 i = 0; i < numFormats; i++) + { + CArcInfoEx item; + item.LibIndex = Libs.Size() - 1; + item.FormatIndex = i; + + RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kName, item.Name)); + + NCOM::CPropVariant prop; + if (ReadProp(getProp, getProp2, i, NArchive::kClassID, prop) != S_OK) + continue; + if (prop.vt != VT_BSTR) + continue; + item.ClassID = *(const GUID *)prop.bstrVal; + prop.Clear(); + + UString ext, addExt; + RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kExtension, ext)); + RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kAddExtension, addExt)); + item.AddExts(ext, addExt); + + ReadBoolProp(getProp, getProp2, i, NArchive::kUpdate, item.UpdateEnabled); + if (item.UpdateEnabled) + ReadBoolProp(getProp, getProp2, i, NArchive::kKeepName, item.KeepName); + + if (ReadProp(getProp, getProp2, i, NArchive::kStartSignature, prop) == S_OK) + if (prop.vt == VT_BSTR) + { + UINT len = ::SysStringByteLen(prop.bstrVal); + item.StartSignature.SetCapacity(len); + memmove(item.StartSignature, prop.bstrVal, len); + } + Formats.Add(item); + } + return S_OK; +} + +#ifdef NEW_FOLDER_INTERFACE +void CCodecLib::LoadIcons() +{ + UString iconTypes = MyLoadStringW((HMODULE)Lib, kIconTypesResId); + UStringVector pairs; + SplitString(iconTypes, pairs); + for (int i = 0; i < pairs.Size(); i++) + { + const UString &s = pairs[i]; + int pos = s.Find(L':'); + if (pos < 0) + continue; + CIconPair iconPair; + const wchar_t *end; + UString num = s.Mid(pos + 1); + iconPair.IconIndex = (UInt32)ConvertStringToUInt64(num, &end); + if (*end != L'\0') + continue; + iconPair.Ext = s.Left(pos); + IconPairs.Add(iconPair); + } +} + +int CCodecLib::FindIconIndex(const UString &ext) const +{ + for (int i = 0; i < IconPairs.Size(); i++) + { + const CIconPair &pair = IconPairs[i]; + if (ext.CompareNoCase(pair.Ext) == 0) + return pair.IconIndex; + } + return -1; +} +#endif + +#ifdef _7ZIP_LARGE_PAGES +extern "C" +{ + extern SIZE_T g_LargePageSize; +} +#endif + +HRESULT CCodecs::LoadDll(const CSysString &dllPath) +{ + { + NDLL::CLibrary library; + if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE)) + return S_OK; + } + Libs.Add(CCodecLib()); + CCodecLib &lib = Libs.Back(); + #ifdef NEW_FOLDER_INTERFACE + lib.Path = dllPath; + #endif + bool used = false; + HRESULT res = S_OK; + if (lib.Lib.Load(dllPath)) + { + #ifdef NEW_FOLDER_INTERFACE + lib.LoadIcons(); + #endif + + #ifdef _7ZIP_LARGE_PAGES + if (g_LargePageSize != 0) + { + SetLargePageModeFunc setLargePageMode = (SetLargePageModeFunc)lib.Lib.GetProcAddress("SetLargePageMode"); + if (setLargePageMode != 0) + setLargePageMode(); + } + #endif + + lib.CreateObject = (CreateObjectFunc)lib.Lib.GetProcAddress("CreateObject"); + if (lib.CreateObject != 0) + { + int startSize = Codecs.Size(); + res = LoadCodecs(); + used = (Codecs.Size() != startSize); + if (res == S_OK) + { + startSize = Formats.Size(); + res = LoadFormats(); + used = used || (Formats.Size() != startSize); + } + } + } + if (!used) + Libs.DeleteBack(); + return res; +} + +HRESULT CCodecs::LoadDllsFromFolder(const CSysString &folderPrefix) +{ + NFile::NFind::CEnumerator enumerator(folderPrefix + CSysString(TEXT("*"))); + NFile::NFind::CFileInfo fileInfo; + while (enumerator.Next(fileInfo)) + { + if (fileInfo.IsDirectory()) + continue; + RINOK(LoadDll(folderPrefix + fileInfo.Name)); + } + return S_OK; +} + +#endif + +#ifndef _SFX +static inline void SetBuffer(CByteBuffer &bb, const Byte *data, int size) +{ + bb.SetCapacity(size); + memmove((Byte *)bb, data, size); +} +#endif + +HRESULT CCodecs::Load() +{ + Formats.Clear(); + #ifdef EXTERNAL_CODECS + Codecs.Clear(); + #endif + for (UInt32 i = 0; i < g_NumArcs; i++) + { + const CArcInfo &arc = *g_Arcs[i]; + CArcInfoEx item; + item.Name = arc.Name; + item.CreateInArchive = arc.CreateInArchive; + item.CreateOutArchive = arc.CreateOutArchive; + item.AddExts(arc.Ext, arc.AddExt); + item.UpdateEnabled = (arc.CreateOutArchive != 0); + item.KeepName = arc.KeepName; + + #ifndef _SFX + SetBuffer(item.StartSignature, arc.Signature, arc.SignatureSize); + #endif + Formats.Add(item); + } + #ifdef EXTERNAL_CODECS + const CSysString baseFolder = GetBaseFolderPrefixFromRegistry(); + RINOK(LoadDll(baseFolder + kMainDll)); + RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName TEXT(STRING_PATH_SEPARATOR))); + RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName TEXT(STRING_PATH_SEPARATOR))); + #endif + return S_OK; +} + +int CCodecs::FindFormatForArchiveName(const UString &archivePath) const +{ + int slashPos1 = archivePath.ReverseFind(L'\\'); + int slashPos2 = archivePath.ReverseFind(L'.'); + int dotPos = archivePath.ReverseFind(L'.'); + if (dotPos < 0 || dotPos < slashPos1 || dotPos < slashPos2) + return -1; + UString ext = archivePath.Mid(dotPos + 1); + for (int i = 0; i < Formats.Size(); i++) + { + const CArcInfoEx &arc = Formats[i]; + if (!arc.UpdateEnabled) + continue; + // if (arc.FindExtension(ext) >= 0) + UString mainExt = arc.GetMainExt(); + if (!mainExt.IsEmpty() && ext.CompareNoCase(mainExt) == 0) + return i; + } + return -1; +} + +int CCodecs::FindFormatForArchiveType(const UString &arcType) const +{ + for (int i = 0; i < Formats.Size(); i++) + { + const CArcInfoEx &arc = Formats[i]; + if (!arc.UpdateEnabled) + continue; + if (arc.Name.CompareNoCase(arcType) == 0) + return i; + } + return -1; +} + +#ifdef EXTERNAL_CODECS + +#ifdef EXPORT_CODECS +extern unsigned int g_NumCodecs; +STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject); +STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); +// STDAPI GetNumberOfMethods(UINT32 *numCodecs); +#endif + +STDMETHODIMP CCodecs::GetNumberOfMethods(UINT32 *numMethods) +{ + *numMethods = + #ifdef EXPORT_CODECS + g_NumCodecs + + #endif + Codecs.Size(); + return S_OK; +} + +STDMETHODIMP CCodecs::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *value) +{ + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + return GetMethodProperty(index, propID, value); + #endif + + const CDllCodecInfo &ci = Codecs[index + #ifdef EXPORT_CODECS + - g_NumCodecs + #endif + ]; + + if (propID == NMethodPropID::kDecoderIsAssigned) + { + NWindows::NCOM::CPropVariant propVariant; + propVariant = ci.DecoderIsAssigned; + propVariant.Detach(value); + return S_OK; + } + if (propID == NMethodPropID::kEncoderIsAssigned) + { + NWindows::NCOM::CPropVariant propVariant; + propVariant = ci.EncoderIsAssigned; + propVariant.Detach(value); + return S_OK; + } + return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value); +} + +STDMETHODIMP CCodecs::CreateDecoder(UINT32 index, const GUID *iid, void **coder) +{ + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + return CreateCoder2(false, index, iid, coder); + #endif + const CDllCodecInfo &ci = Codecs[index + #ifdef EXPORT_CODECS + - g_NumCodecs + #endif + ]; + if (ci.DecoderIsAssigned) + return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder); + return S_OK; +} + +STDMETHODIMP CCodecs::CreateEncoder(UINT32 index, const GUID *iid, void **coder) +{ + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + return CreateCoder2(true, index, iid, coder); + #endif + const CDllCodecInfo &ci = Codecs[index + #ifdef EXPORT_CODECS + - g_NumCodecs + #endif + ]; + if (ci.EncoderIsAssigned) + return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder); + return S_OK; +} + +HRESULT CCodecs::CreateCoder(const UString &name, bool encode, CMyComPtr &coder) const +{ + for (int i = 0; i < Codecs.Size(); i++) + { + const CDllCodecInfo &codec = Codecs[i]; + if (encode && !codec.EncoderIsAssigned || !encode && !codec.DecoderIsAssigned) + continue; + const CCodecLib &lib = Libs[codec.LibIndex]; + UString res; + NWindows::NCOM::CPropVariant prop; + RINOK(lib.GetMethodProperty(codec.CodecIndex, NMethodPropID::kName, &prop)); + if (prop.vt == VT_BSTR) + res = prop.bstrVal; + else if (prop.vt != VT_EMPTY) + continue; + if (name.CompareNoCase(res) == 0) + return lib.CreateObject(encode ? &codec.Encoder : &codec.Decoder, &IID_ICompressCoder, (void **)&coder); + } + return CLASS_E_CLASSNOTAVAILABLE; +} + +int CCodecs::GetCodecLibIndex(UInt32 index) +{ + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + return -1; + #endif + #ifdef EXTERNAL_CODECS + const CDllCodecInfo &ci = Codecs[index + #ifdef EXPORT_CODECS + - g_NumCodecs + #endif + ]; + return ci.LibIndex; + #else + return -1; + #endif +} + +bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index) +{ + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + { + NWindows::NCOM::CPropVariant prop; + if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK) + if (prop.vt != VT_EMPTY) + return true; + return false; + } + #endif + #ifdef EXTERNAL_CODECS + const CDllCodecInfo &ci = Codecs[index + #ifdef EXPORT_CODECS + - g_NumCodecs + #endif + ]; + return ci.EncoderIsAssigned; + #else + return false; + #endif +} + +HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id) +{ + UString s; + NWindows::NCOM::CPropVariant prop; + RINOK(GetProperty(index, NMethodPropID::kID, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + id = prop.uhVal.QuadPart; + return S_OK; +} + +UString CCodecs::GetCodecName(UInt32 index) +{ + UString s; + NWindows::NCOM::CPropVariant prop; + if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK) + if (prop.vt == VT_BSTR) + s = prop.bstrVal; + return s; +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.h new file mode 100644 index 0000000..231680b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/LoadCodecs.h @@ -0,0 +1,215 @@ +// LoadCodecs.h + +#ifndef __LOADCODECS_H +#define __LOADCODECS_H + +#include "../../../Common/Types.h" +#include "../../../Common/MyCom.h" +#include "../../../Common/MyString.h" +#include "../../../Common/Buffer.h" +#include "../../ICoder.h" + +#ifdef EXTERNAL_CODECS +#include "../../../Windows/DLL.h" +#endif + +struct CDllCodecInfo +{ + CLSID Encoder; + CLSID Decoder; + bool EncoderIsAssigned; + bool DecoderIsAssigned; + int LibIndex; + UInt32 CodecIndex; +}; + +#include "../../Archive/IArchive.h" + +typedef IInArchive * (*CreateInArchiveP)(); +typedef IOutArchive * (*CreateOutArchiveP)(); + +struct CArcExtInfo +{ + UString Ext; + UString AddExt; + CArcExtInfo() {} + CArcExtInfo(const UString &ext): Ext(ext) {} + CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {} +}; + + +struct CArcInfoEx +{ + #ifdef EXTERNAL_CODECS + int LibIndex; + UInt32 FormatIndex; + CLSID ClassID; + #endif + bool UpdateEnabled; + CreateInArchiveP CreateInArchive; + CreateOutArchiveP CreateOutArchive; + UString Name; + CObjectVector Exts; + #ifndef _SFX + CByteBuffer StartSignature; + // CByteBuffer FinishSignature; + #ifdef NEW_FOLDER_INTERFACE + UStringVector AssociateExts; + #endif + #endif + bool KeepName; + UString GetMainExt() const + { + if (Exts.IsEmpty()) + return UString(); + return Exts[0].Ext; + } + int FindExtension(const UString &ext) const + { + for (int i = 0; i < Exts.Size(); i++) + if (ext.CompareNoCase(Exts[i].Ext) == 0) + return i; + return -1; + } + UString GetAllExtensions() const + { + UString s; + for (int i = 0; i < Exts.Size(); i++) + { + if (i > 0) + s += ' '; + s += Exts[i].Ext; + } + return s; + } + + void AddExts(const wchar_t* ext, const wchar_t* addExt); + + CArcInfoEx(): + #ifdef EXTERNAL_CODECS + LibIndex(-1), + #endif + UpdateEnabled(false), + CreateInArchive(0), CreateOutArchive(0), + KeepName(false) + #ifndef _SFX + #endif + {} +}; + +#ifdef EXTERNAL_CODECS +typedef UInt32 (WINAPI *GetMethodPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT *value); +typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *interfaceID, void **outObject); + + +struct CCodecLib +{ + NWindows::NDLL::CLibrary Lib; + GetMethodPropertyFunc GetMethodProperty; + CreateObjectFunc CreateObject; + #ifdef NEW_FOLDER_INTERFACE + struct CIconPair + { + UString Ext; + UInt32 IconIndex; + }; + CSysString Path; + CObjectVector IconPairs; + void LoadIcons(); + int FindIconIndex(const UString &ext) const; + #endif + CCodecLib(): GetMethodProperty(0) {} +}; +#endif + +class CCodecs: + #ifdef EXTERNAL_CODECS + public ICompressCodecsInfo, + #else + public IUnknown, + #endif + public CMyUnknownImp +{ +public: + #ifdef EXTERNAL_CODECS + CObjectVector Libs; + CObjectVector Codecs; + HRESULT LoadCodecs(); + HRESULT LoadFormats(); + HRESULT LoadDll(const CSysString &path); + HRESULT LoadDllsFromFolder(const CSysString &folderPrefix); + + HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const + { + return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive); + } + #endif + +public: + CObjectVector Formats; + HRESULT Load(); + int FindFormatForArchiveName(const UString &archivePath) const; + int FindFormatForArchiveType(const UString &arcType) const; + + MY_UNKNOWN_IMP + + #ifdef EXTERNAL_CODECS + STDMETHOD(GetNumberOfMethods)(UINT32 *numMethods); + STDMETHOD(GetProperty)(UINT32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(CreateDecoder)(UINT32 index, const GUID *interfaceID, void **coder); + STDMETHOD(CreateEncoder)(UINT32 index, const GUID *interfaceID, void **coder); + #endif + + int GetCodecLibIndex(UInt32 index); + bool GetCodecEncoderIsAssigned(UInt32 index); + HRESULT GetCodecId(UInt32 index, UInt64 &id); + UString GetCodecName(UInt32 index); + + HRESULT CreateInArchive(int formatIndex, CMyComPtr &archive) const + { + const CArcInfoEx &ai = Formats[formatIndex]; + #ifdef EXTERNAL_CODECS + if (ai.LibIndex < 0) + #endif + { + archive = ai.CreateInArchive(); + return S_OK; + } + #ifdef EXTERNAL_CODECS + return CreateArchiveHandler(ai, (void **)&archive, false); + #endif + } + HRESULT CreateOutArchive(int formatIndex, CMyComPtr &archive) const + { + const CArcInfoEx &ai = Formats[formatIndex]; + #ifdef EXTERNAL_CODECS + if (ai.LibIndex < 0) + #endif + { + archive = ai.CreateOutArchive(); + return S_OK; + } + #ifdef EXTERNAL_CODECS + return CreateArchiveHandler(ai, (void **)&archive, true); + #endif + } + int FindOutFormatFromName(const UString &name) const + { + for (int i = 0; i < Formats.Size(); i++) + { + const CArcInfoEx &arc = Formats[i]; + if (!arc.UpdateEnabled) + continue; + if (arc.Name.CompareNoCase(name) == 0) + return i; + } + return -1; + } + + #ifdef EXTERNAL_CODECS + HRESULT CreateCoder(const UString &name, bool encode, CMyComPtr &coder) const; + #endif + +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.cpp new file mode 100644 index 0000000..2874f6a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.cpp @@ -0,0 +1,461 @@ +// OpenArchive.cpp + +#include "StdAfx.h" + +#include "OpenArchive.h" + +#include "Common/Wildcard.h" + +#include "Windows/FileName.h" +#include "Windows/FileDir.h" +#include "Windows/Defs.h" +#include "Windows/PropVariant.h" + +#include "../../Common/FileStreams.h" +#include "../../Common/StreamUtils.h" + +#include "Common/StringConvert.h" + +#include "DefaultName.h" + +using namespace NWindows; + +HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result) +{ + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidPath, &prop)); + if(prop.vt == VT_BSTR) + result = prop.bstrVal; + else if (prop.vt == VT_EMPTY) + result.Empty(); + else + return E_FAIL; + return S_OK; +} + +HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result) +{ + RINOK(GetArchiveItemPath(archive, index, result)); + if (result.IsEmpty()) + result = defaultName; + return S_OK; +} + +HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index, + const FILETIME &defaultFileTime, FILETIME &fileTime) +{ + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidLastWriteTime, &prop)); + if (prop.vt == VT_FILETIME) + fileTime = prop.filetime; + else if (prop.vt == VT_EMPTY) + fileTime = defaultFileTime; + else + return E_FAIL; + return S_OK; +} + +HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) +{ + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, propID, &prop)); + if(prop.vt == VT_BOOL) + result = VARIANT_BOOLToBool(prop.boolVal); + else if (prop.vt == VT_EMPTY) + result = false; + else + return E_FAIL; + return S_OK; +} + +HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) +{ + return IsArchiveItemProp(archive, index, kpidIsFolder, result); +} + +HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result) +{ + return IsArchiveItemProp(archive, index, kpidIsAnti, result); +} + +// Static-SFX (for Linux) can be big. +const UInt64 kMaxCheckStartPosition = 1 << 22; + +HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback) +{ + CInFileStream *inStreamSpec = new CInFileStream; + CMyComPtr inStream(inStreamSpec); + inStreamSpec->Open(fileName); + return archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback); +} + +#ifndef _SFX +static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) +{ + for (size_t i = 0; i < size; i++) + if (p1[i] != p2[i]) + return false; + return true; +} +#endif + +HRESULT OpenArchive( + CCodecs *codecs, + IInStream *inStream, + const UString &fileName, + IInArchive **archiveResult, + int &formatIndex, + UString &defaultItemName, + IArchiveOpenCallback *openArchiveCallback) +{ + *archiveResult = NULL; + UString extension; + { + int dotPos = fileName.ReverseFind(L'.'); + if (dotPos >= 0) + extension = fileName.Mid(dotPos + 1); + } + CIntVector orderIndices; + int i; + int numFinded = 0; + for (i = 0; i < codecs->Formats.Size(); i++) + if (codecs->Formats[i].FindExtension(extension) >= 0) + orderIndices.Insert(numFinded++, i); + else + orderIndices.Add(i); + + #ifndef _SFX + if (numFinded != 1) + { + CIntVector orderIndices2; + CByteBuffer byteBuffer; + const UInt32 kBufferSize = (200 << 10); + byteBuffer.SetCapacity(kBufferSize); + Byte *buffer = byteBuffer; + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + UInt32 processedSize; + RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize)); + for (UInt32 pos = 0; pos < processedSize; pos++) + { + for (int i = 0; i < orderIndices.Size(); i++) + { + int index = orderIndices[i]; + const CArcInfoEx &ai = codecs->Formats[index]; + const CByteBuffer &sig = ai.StartSignature; + if (sig.GetCapacity() == 0) + continue; + if (pos + sig.GetCapacity() > processedSize) + continue; + if (TestSignature(buffer + pos, sig, sig.GetCapacity())) + { + orderIndices2.Add(index); + orderIndices.Delete(i--); + } + } + } + orderIndices2 += orderIndices; + orderIndices = orderIndices2; + } + else if (extension == L"000" || extension == L"001") + { + CByteBuffer byteBuffer; + const UInt32 kBufferSize = (1 << 10); + byteBuffer.SetCapacity(kBufferSize); + Byte *buffer = byteBuffer; + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + UInt32 processedSize; + RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize)); + if (processedSize >= 16) + { + Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}; + if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] && 1) != 0) + { + for (int i = 0; i < orderIndices.Size(); i++) + { + int index = orderIndices[i]; + const CArcInfoEx &ai = codecs->Formats[index]; + if (ai.Name.CompareNoCase(L"rar") != 0) + continue; + orderIndices.Delete(i--); + orderIndices.Insert(0, index); + break; + } + } + } + } + #endif + + HRESULT badResult = S_OK; + for(i = 0; i < orderIndices.Size(); i++) + { + inStream->Seek(0, STREAM_SEEK_SET, NULL); + + CMyComPtr archive; + + formatIndex = orderIndices[i]; + RINOK(codecs->CreateInArchive(formatIndex, archive)); + if (!archive) + continue; + + #ifdef EXTERNAL_CODECS + { + CMyComPtr setCompressCodecsInfo; + archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); + } + } + #endif + + HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback); + if (result == S_FALSE) + continue; + if(result != S_OK) + { + badResult = result; + if(result == E_ABORT) + break; + continue; + } + *archiveResult = archive.Detach(); + const CArcInfoEx &format = codecs->Formats[formatIndex]; + if (format.Exts.Size() == 0) + { + defaultItemName = GetDefaultName2(fileName, L"", L""); + } + else + { + int subExtIndex = format.FindExtension(extension); + if (subExtIndex < 0) + subExtIndex = 0; + defaultItemName = GetDefaultName2(fileName, + format.Exts[subExtIndex].Ext, + format.Exts[subExtIndex].AddExt); + } + return S_OK; + } + if (badResult != S_OK) + return badResult; + return S_FALSE; +} + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &filePath, + IInArchive **archiveResult, + int &formatIndex, + UString &defaultItemName, + IArchiveOpenCallback *openArchiveCallback) +{ + CInFileStream *inStreamSpec = new CInFileStream; + CMyComPtr inStream(inStreamSpec); + if (!inStreamSpec->Open(filePath)) + return GetLastError(); + return OpenArchive(codecs, inStream, ExtractFileNameFromPath(filePath), + archiveResult, formatIndex, + defaultItemName, openArchiveCallback); +} + +static void MakeDefaultName(UString &name) +{ + int dotPos = name.ReverseFind(L'.'); + if (dotPos < 0) + return; + UString ext = name.Mid(dotPos + 1); + if (ext.IsEmpty()) + return; + for (int pos = 0; pos < ext.Length(); pos++) + if (ext[pos] < L'0' || ext[pos] > L'9') + return; + name = name.Left(dotPos); +} + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &fileName, + IInArchive **archive0, + IInArchive **archive1, + int &formatIndex0, + int &formatIndex1, + UString &defaultItemName0, + UString &defaultItemName1, + IArchiveOpenCallback *openArchiveCallback) +{ + HRESULT result = OpenArchive(codecs, fileName, + archive0, formatIndex0, defaultItemName0, openArchiveCallback); + RINOK(result); + CMyComPtr getStream; + result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream); + if (result != S_OK || getStream == 0) + return S_OK; + + CMyComPtr subSeqStream; + result = getStream->GetStream(0, &subSeqStream); + if (result != S_OK) + return S_OK; + + CMyComPtr subStream; + if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK) + return S_OK; + if (!subStream) + return S_OK; + + UInt32 numItems; + RINOK((*archive0)->GetNumberOfItems(&numItems)); + if (numItems < 1) + return S_OK; + + UString subPath; + RINOK(GetArchiveItemPath(*archive0, 0, subPath)) + if (subPath.IsEmpty()) + { + MakeDefaultName(defaultItemName0); + subPath = defaultItemName0; + const CArcInfoEx &format = codecs->Formats[formatIndex0]; + if (format.Name.CompareNoCase(L"7z") == 0) + { + if (subPath.Right(3).CompareNoCase(L".7z") != 0) + subPath += L".7z"; + } + } + else + subPath = ExtractFileNameFromPath(subPath); + + CMyComPtr setSubArchiveName; + openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); + if (setSubArchiveName) + setSubArchiveName->SetSubArchiveName(subPath); + + result = OpenArchive(codecs, subStream, subPath, + archive1, formatIndex1, defaultItemName1, openArchiveCallback); + return S_OK; +} + +static void SetCallback(const UString &archiveName, + IOpenCallbackUI *openCallbackUI, CMyComPtr &openCallback) +{ + COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; + openCallback = openCallbackSpec; + openCallbackSpec->Callback = openCallbackUI; + + UString fullName; + int fileNamePartStartIndex; + NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); + openCallbackSpec->Init( + fullName.Left(fileNamePartStartIndex), + fullName.Mid(fileNamePartStartIndex)); +} + +HRESULT MyOpenArchive( + CCodecs *codecs, + const UString &archiveName, + IInArchive **archive, UString &defaultItemName, IOpenCallbackUI *openCallbackUI) +{ + CMyComPtr openCallback; + SetCallback(archiveName, openCallbackUI, openCallback); + int formatInfo; + return OpenArchive(codecs, archiveName, archive, formatInfo, defaultItemName, openCallback); +} + +HRESULT MyOpenArchive( + CCodecs *codecs, + const UString &archiveName, + IInArchive **archive0, + IInArchive **archive1, + UString &defaultItemName0, + UString &defaultItemName1, + UStringVector &volumePaths, + UInt64 &volumesSize, + IOpenCallbackUI *openCallbackUI) +{ + volumesSize = 0; + COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; + CMyComPtr openCallback = openCallbackSpec; + openCallbackSpec->Callback = openCallbackUI; + + UString fullName; + int fileNamePartStartIndex; + NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); + UString prefix = fullName.Left(fileNamePartStartIndex); + UString name = fullName.Mid(fileNamePartStartIndex); + openCallbackSpec->Init(prefix, name); + + int formatIndex0, formatIndex1; + RINOK(OpenArchive(codecs, archiveName, + archive0, + archive1, + formatIndex0, + formatIndex1, + defaultItemName0, + defaultItemName1, + openCallback)); + volumePaths.Add(prefix + name); + for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) + volumePaths.Add(prefix + openCallbackSpec->FileNames[i]); + volumesSize = openCallbackSpec->TotalSize; + return S_OK; +} + +HRESULT CArchiveLink::Close() +{ + if (Archive1 != 0) + RINOK(Archive1->Close()); + if (Archive0 != 0) + RINOK(Archive0->Close()); + IsOpen = false; + return S_OK; +} + +void CArchiveLink::Release() +{ + IsOpen = false; + Archive1.Release(); + Archive0.Release(); +} + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &archiveName, + CArchiveLink &archiveLink, + IArchiveOpenCallback *openCallback) +{ + HRESULT res = OpenArchive(codecs, archiveName, + &archiveLink.Archive0, &archiveLink.Archive1, + archiveLink.FormatIndex0, archiveLink.FormatIndex1, + archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, + openCallback); + archiveLink.IsOpen = (res == S_OK); + return res; +} + +HRESULT MyOpenArchive(CCodecs *codecs, + const UString &archiveName, + CArchiveLink &archiveLink, + IOpenCallbackUI *openCallbackUI) +{ + HRESULT res = MyOpenArchive(codecs, archiveName, + &archiveLink.Archive0, &archiveLink.Archive1, + archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, + archiveLink.VolumePaths, + archiveLink.VolumesSize, + openCallbackUI); + archiveLink.IsOpen = (res == S_OK); + return res; +} + +HRESULT ReOpenArchive(CCodecs *codecs, CArchiveLink &archiveLink, const UString &fileName) +{ + if (archiveLink.GetNumLevels() > 1) + return E_NOTIMPL; + + if (archiveLink.GetNumLevels() == 0) + return MyOpenArchive(codecs, fileName, archiveLink, 0); + + CMyComPtr openCallback; + SetCallback(fileName, NULL, openCallback); + + HRESULT res = ReOpenArchive(archiveLink.GetArchive(), fileName, openCallback); + archiveLink.IsOpen = (res == S_OK); + return res; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.h new file mode 100644 index 0000000..7b42446 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/OpenArchive.h @@ -0,0 +1,130 @@ +// OpenArchive.h + +#ifndef __OPENARCHIVE_H +#define __OPENARCHIVE_H + +#include "Common/MyString.h" +#include "Windows/FileFind.h" + +#include "../../Archive/IArchive.h" +#include "LoadCodecs.h" +#include "ArchiveOpenCallback.h" + +HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result); +HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result); +HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index, + const FILETIME &defaultFileTime, FILETIME &fileTime); +HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result); +HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result); +HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result); + +struct ISetSubArchiveName +{ + virtual void SetSubArchiveName(const wchar_t *name) = 0; +}; + +HRESULT OpenArchive( + CCodecs *codecs, + IInStream *inStream, + const UString &fileName, + IInArchive **archiveResult, + int &formatIndex, + UString &defaultItemName, + IArchiveOpenCallback *openArchiveCallback); + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &filePath, + IInArchive **archive, + int &formatIndex, + UString &defaultItemName, + IArchiveOpenCallback *openArchiveCallback); + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &filePath, + IInArchive **archive0, + IInArchive **archive1, + int &formatIndex0, + int &formatIndex1, + UString &defaultItemName0, + UString &defaultItemName1, + IArchiveOpenCallback *openArchiveCallback); + + +HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback); + +HRESULT MyOpenArchive( + CCodecs *codecs, + const UString &archiveName, + IInArchive **archive, + UString &defaultItemName, + IOpenCallbackUI *openCallbackUI); + +HRESULT MyOpenArchive( + CCodecs *codecs, + const UString &archiveName, + IInArchive **archive0, + IInArchive **archive1, + UString &defaultItemName0, + UString &defaultItemName1, + UStringVector &volumePaths, + UInt64 &volumesSize, + IOpenCallbackUI *openCallbackUI); + +struct CArchiveLink +{ + CMyComPtr Archive0; + CMyComPtr Archive1; + UString DefaultItemName0; + UString DefaultItemName1; + + int FormatIndex0; + int FormatIndex1; + + UStringVector VolumePaths; + + UInt64 VolumesSize; + + int GetNumLevels() const + { + int result = 0; + if (Archive0) + { + result++; + if (Archive1) + result++; + } + return result; + } + + bool IsOpen; + + CArchiveLink(): IsOpen(false), VolumesSize(0) {}; + + IInArchive *GetArchive() { return Archive1 != 0 ? Archive1: Archive0; } + UString GetDefaultItemName() { return Archive1 != 0 ? DefaultItemName1: DefaultItemName0; } + int GetArchiverIndex() const { return Archive1 != 0 ? FormatIndex1: FormatIndex0; } + HRESULT Close(); + void Release(); +}; + +HRESULT OpenArchive( + CCodecs *codecs, + const UString &archiveName, + CArchiveLink &archiveLink, + IArchiveOpenCallback *openCallback); + +HRESULT MyOpenArchive( + CCodecs *codecs, + const UString &archiveName, + CArchiveLink &archiveLink, + IOpenCallbackUI *openCallbackUI); + +HRESULT ReOpenArchive( + CCodecs *codecs, + CArchiveLink &archiveLink, + const UString &fileName); + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp new file mode 100644 index 0000000..7659688 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp @@ -0,0 +1,89 @@ +// PropIDUtils.cpp + +#include "StdAfx.h" + +#include "PropIDUtils.h" + +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/FileFind.h" +#include "Windows/PropVariantConversions.h" + +#include "../../PropID.h" + +using namespace NWindows; + +static UString ConvertUInt32ToString(UInt32 value) +{ + wchar_t buffer[32]; + ConvertUInt64ToString(value, buffer); + return buffer; +} + +static void ConvertUInt32ToHex(UInt32 value, wchar_t *s) +{ + for (int i = 0; i < 8; i++) + { + int t = value & 0xF; + value >>= 4; + s[7 - i] = (wchar_t)((t < 10) ? (L'0' + t) : (L'A' + (t - 10))); + } + s[8] = L'\0'; +} + +UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full) +{ + switch(propID) + { + case kpidCreationTime: + case kpidLastWriteTime: + case kpidLastAccessTime: + { + if (propVariant.vt != VT_FILETIME) + return UString(); // It is error; + FILETIME localFileTime; + if (propVariant.filetime.dwHighDateTime == 0 && + propVariant.filetime.dwLowDateTime == 0) + return UString(); + if (!::FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime)) + return UString(); // It is error; + return ConvertFileTimeToString(localFileTime, true, full); + } + case kpidCRC: + { + if(propVariant.vt != VT_UI4) + break; + wchar_t temp[12]; + ConvertUInt32ToHex(propVariant.ulVal, temp); + return temp; + } + case kpidAttributes: + { + if(propVariant.vt != VT_UI4) + break; + UString result; + UInt32 attributes = propVariant.ulVal; + if (NFile::NFind::NAttributes::IsReadOnly(attributes)) result += L'R'; + if (NFile::NFind::NAttributes::IsHidden(attributes)) result += L'H'; + if (NFile::NFind::NAttributes::IsSystem(attributes)) result += L'S'; + if (NFile::NFind::NAttributes::IsDirectory(attributes)) result += L'D'; + if (NFile::NFind::NAttributes::IsArchived(attributes)) result += L'A'; + if (NFile::NFind::NAttributes::IsCompressed(attributes)) result += L'C'; + if (NFile::NFind::NAttributes::IsEncrypted(attributes)) result += L'E'; + return result; + } + case kpidDictionarySize: + { + if(propVariant.vt != VT_UI4) + break; + UInt32 size = propVariant.ulVal; + if (size % (1 << 20) == 0) + return ConvertUInt32ToString(size >> 20) + L"MB"; + if (size % (1 << 10) == 0) + return ConvertUInt32ToString(size >> 10) + L"KB"; + return ConvertUInt32ToString(size); + } + } + return ConvertPropVariantToString(propVariant); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.h new file mode 100644 index 0000000..1d82097 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/PropIDUtils.h @@ -0,0 +1,10 @@ +// PropIDUtils.h + +#ifndef __PROPIDUTILS_H +#define __PROPIDUTILS_H + +#include "Common/MyString.h" + +UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full = true); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Property.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Property.h new file mode 100644 index 0000000..9fd340c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Property.h @@ -0,0 +1,14 @@ +// Property.h + +#ifndef __PROPERTY_H +#define __PROPERTY_H + +#include "Common/MyString.h" + +struct CProperty +{ + UString Name; + UString Value; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.cpp new file mode 100644 index 0000000..b1434ac --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.cpp @@ -0,0 +1,65 @@ +// SetProperties.cpp + +#include "StdAfx.h" + +#include "SetProperties.h" + +#include "Windows/PropVariant.h" +#include "Common/MyString.h" +#include "Common/StringToInt.h" +#include "Common/MyCom.h" + +#include "../../Archive/IArchive.h" + +using namespace NWindows; +using namespace NCOM; + +static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop) +{ + const wchar_t *endPtr; + UInt64 result = ConvertStringToUInt64(s, &endPtr); + if (endPtr - (const wchar_t *)s != s.Length()) + prop = s; + else if (result <= 0xFFFFFFFF) + prop = (UInt32)result; + else + prop = result; +} + +HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties) +{ + if (properties.IsEmpty()) + return S_OK; + CMyComPtr setProperties; + unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties); + if (!setProperties) + return S_OK; + + UStringVector realNames; + CPropVariant *values = new CPropVariant[properties.Size()]; + try + { + int i; + for(i = 0; i < properties.Size(); i++) + { + const CProperty &property = properties[i]; + NCOM::CPropVariant propVariant; + if (!property.Value.IsEmpty()) + ParseNumberString(property.Value, propVariant); + realNames.Add(property.Name); + values[i] = propVariant; + } + CRecordVector names; + for(i = 0; i < realNames.Size(); i++) + names.Add((const wchar_t *)realNames[i]); + + RINOK(setProperties->SetProperties(&names.Front(), values, names.Size())); + } + catch(...) + { + delete []values; + throw; + } + delete []values; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.h new file mode 100644 index 0000000..892f1a2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SetProperties.h @@ -0,0 +1,10 @@ +// SetProperties.h + +#ifndef __SETPROPERTIES_H +#define __SETPROPERTIES_H + +#include "Property.h" + +HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.cpp new file mode 100644 index 0000000..061e777 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.cpp @@ -0,0 +1,22 @@ +// SortUtils.cpp + +#include "StdAfx.h" + +#include "SortUtils.h" +#include "Common/Wildcard.h" + +static int CompareStrings(const int *p1, const int *p2, void *param) +{ + const UStringVector &strings = *(const UStringVector *)param; + return CompareFileNames(strings[*p1], strings[*p2]); +} + +void SortFileNames(const UStringVector &strings, CIntVector &indices) +{ + indices.Clear(); + int numItems = strings.Size(); + indices.Reserve(numItems); + for(int i = 0; i < numItems; i++) + indices.Add(i); + indices.Sort(CompareStrings, (void *)&strings); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.h new file mode 100644 index 0000000..e152246 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/SortUtils.h @@ -0,0 +1,10 @@ +// SortUtils.h + +#ifndef __SORTUTLS_H +#define __SORTUTLS_H + +#include "Common/MyString.h" + +void SortFileNames(const UStringVector &strings, CIntVector &indices); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.cpp new file mode 100644 index 0000000..eeaec18 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.cpp @@ -0,0 +1,22 @@ +// TempFiles.cpp + +#include "StdAfx.h" + +#include "TempFiles.h" + +#include "Windows/FileDir.h" +#include "Windows/FileIO.h" + +using namespace NWindows; +using namespace NFile; + +void CTempFiles::Clear() +{ + while(!Paths.IsEmpty()) + { + NDirectory::DeleteFileAlways((LPCWSTR)Paths.Back()); + Paths.DeleteBack(); + } +} + + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.h new file mode 100644 index 0000000..eb474a7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/TempFiles.h @@ -0,0 +1,16 @@ +// TempFiles.h + +#ifndef __TEMPFILES_H +#define __TEMPFILES_H + +#include "Common/MyString.h" + +class CTempFiles +{ + void Clear(); +public: + UStringVector Paths; + ~CTempFiles() { Clear(); } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.cpp new file mode 100644 index 0000000..ec5ebc8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.cpp @@ -0,0 +1,852 @@ +// Update.cpp + +#include "StdAfx.h" + +#ifdef _WIN32 +#include +#endif + +#include "Update.h" + +#include "Common/IntToString.h" +#include "Common/StringConvert.h" +#include "Common/CommandLineParser.h" + +#ifdef _WIN32 +#include "Windows/DLL.h" +#endif + +#include "Windows/Defs.h" +#include "Windows/FileDir.h" +#include "Windows/FileFind.h" +#include "Windows/FileName.h" +#include "Windows/PropVariant.h" +#include "Windows/PropVariantConversions.h" +// #include "Windows/Synchronization.h" + +#include "../../Common/FileStreams.h" +#include "../../Compress/Copy/CopyCoder.h" + +#include "../Common/DirItem.h" +#include "../Common/EnumDirItems.h" +#include "../Common/UpdateProduce.h" +#include "../Common/OpenArchive.h" + +#include "TempFiles.h" +#include "UpdateCallback.h" +#include "EnumDirItems.h" +#include "SetProperties.h" + +static const char *kUpdateIsNotSupoorted = + "update operations are not supported for this archive"; + +using namespace NCommandLineParser; +using namespace NWindows; +using namespace NCOM; +using namespace NFile; +using namespace NName; + +static const wchar_t *kTempFolderPrefix = L"7zE"; + +using namespace NUpdateArchive; + +static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream) +{ + CMyComPtr copyCoder = new NCompress::CCopyCoder; + return copyCoder->Code(inStream, outStream, NULL, NULL, NULL); +} + +class COutMultiVolStream: + public IOutStream, + public CMyUnknownImp +{ + int _streamIndex; // required stream + UInt64 _offsetPos; // offset from start of _streamIndex index + UInt64 _absPos; + UInt64 _length; + + struct CSubStreamInfo + { + COutFileStream *StreamSpec; + CMyComPtr Stream; + UString Name; + UInt64 Pos; + UInt64 RealSize; + }; + CObjectVector Streams; +public: + // CMyComPtr VolumeCallback; + CRecordVector Sizes; + UString Prefix; + CTempFiles *TempFiles; + + void Init() + { + _streamIndex = 0; + _offsetPos = 0; + _absPos = 0; + _length = 0; + } + + HRESULT Close(); + + MY_UNKNOWN_IMP1(IOutStream) + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + STDMETHOD(SetSize)(Int64 newSize); +}; + +// static NSynchronization::CCriticalSection g_TempPathsCS; + +HRESULT COutMultiVolStream::Close() +{ + HRESULT res = S_OK; + for (int i = 0; i < Streams.Size(); i++) + { + CSubStreamInfo &s = Streams[i]; + if (s.StreamSpec) + { + HRESULT res2 = s.StreamSpec->Close(); + if (res2 != S_OK) + res = res2; + } + } + return res; +} + +STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + while(size > 0) + { + if (_streamIndex >= Streams.Size()) + { + CSubStreamInfo subStream; + + wchar_t temp[32]; + ConvertUInt64ToString(_streamIndex + 1, temp); + UString res = temp; + while (res.Length() < 3) + res = UString(L'0') + res; + UString name = Prefix + res; + subStream.StreamSpec = new COutFileStream; + subStream.Stream = subStream.StreamSpec; + if(!subStream.StreamSpec->Create(name, false)) + return ::GetLastError(); + { + // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); + TempFiles->Paths.Add(name); + } + + subStream.Pos = 0; + subStream.RealSize = 0; + subStream.Name = name; + Streams.Add(subStream); + continue; + } + CSubStreamInfo &subStream = Streams[_streamIndex]; + + int index = _streamIndex; + if (index >= Sizes.Size()) + index = Sizes.Size() - 1; + UInt64 volSize = Sizes[index]; + + if (_offsetPos >= volSize) + { + _offsetPos -= volSize; + _streamIndex++; + continue; + } + if (_offsetPos != subStream.Pos) + { + // CMyComPtr outStream; + // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); + RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); + subStream.Pos = _offsetPos; + } + + UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos); + UInt32 realProcessed; + RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); + data = (void *)((Byte *)data + realProcessed); + size -= realProcessed; + subStream.Pos += realProcessed; + _offsetPos += realProcessed; + _absPos += realProcessed; + if (_absPos > _length) + _length = _absPos; + if (_offsetPos > subStream.RealSize) + subStream.RealSize = _offsetPos; + if(processedSize != NULL) + *processedSize += realProcessed; + if (subStream.Pos == volSize) + { + _streamIndex++; + _offsetPos = 0; + } + if (realProcessed == 0 && curSize != 0) + return E_FAIL; + break; + } + return S_OK; +} + +STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if(seekOrigin >= 3) + return STG_E_INVALIDFUNCTION; + switch(seekOrigin) + { + case STREAM_SEEK_SET: + _absPos = offset; + break; + case STREAM_SEEK_CUR: + _absPos += offset; + break; + case STREAM_SEEK_END: + _absPos = _length + offset; + break; + } + _offsetPos = _absPos; + if (newPosition != NULL) + *newPosition = _absPos; + _streamIndex = 0; + return S_OK; +} + +STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize) +{ + if (newSize < 0) + return E_INVALIDARG; + int i = 0; + while (i < Streams.Size()) + { + CSubStreamInfo &subStream = Streams[i++]; + if ((UInt64)newSize < subStream.RealSize) + { + RINOK(subStream.Stream->SetSize(newSize)); + subStream.RealSize = newSize; + break; + } + newSize -= subStream.RealSize; + } + while (i < Streams.Size()) + { + { + CSubStreamInfo &subStream = Streams.Back(); + subStream.Stream.Release(); + NDirectory::DeleteFileAlways(subStream.Name); + } + Streams.DeleteBack(); + } + _offsetPos = _absPos; + _streamIndex = 0; + _length = newSize; + return S_OK; +} + +static const wchar_t *kDefaultArchiveType = L"7z"; +static const wchar_t *kSFXExtension = + #ifdef _WIN32 + L"exe"; + #else + L""; + #endif + +bool CUpdateOptions::Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType) +{ + if (!arcType.IsEmpty()) + MethodMode.FormatIndex = codecs->FindFormatForArchiveType(arcType); + else + { + MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath); + if (MethodMode.FormatIndex < 0) + MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType); + } + if (MethodMode.FormatIndex < 0) + return false; + const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex]; + UString typeExt = arcInfo.GetMainExt(); + UString ext = typeExt; + if (SfxMode) + ext = kSFXExtension; + ArchivePath.BaseExtension = ext; + ArchivePath.VolExtension = typeExt; + ArchivePath.ParseFromPath(arcPath); + for (int i = 0; i < Commands.Size(); i++) + { + CUpdateArchiveCommand &uc = Commands[i]; + uc.ArchivePath.BaseExtension = ext; + uc.ArchivePath.VolExtension = typeExt; + uc.ArchivePath.ParseFromPath(uc.UserArchivePath); + } + return true; +} + + +static HRESULT Compress( + CCodecs *codecs, + const CActionSet &actionSet, + IInArchive *archive, + const CCompressionMethodMode &compressionMethod, + CArchivePath &archivePath, + const CObjectVector &archiveItems, + bool shareForWrite, + bool stdInMode, + /* const UString & stdInFileName, */ + bool stdOutMode, + const CObjectVector &dirItems, + bool sfxMode, + const UString &sfxModule, + const CRecordVector &volumesSizes, + CTempFiles &tempFiles, + CUpdateErrorInfo &errorInfo, + IUpdateCallbackUI *callback) +{ + CMyComPtr outArchive; + if(archive != NULL) + { + CMyComPtr archive2 = archive; + HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); + if(result != S_OK) + throw kUpdateIsNotSupoorted; + } + else + { + RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive)); + + #ifdef EXTERNAL_CODECS + { + CMyComPtr setCompressCodecsInfo; + outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); + } + } + #endif + } + if (outArchive == 0) + throw kUpdateIsNotSupoorted; + + NFileTimeType::EEnum fileTimeType; + UInt32 value; + RINOK(outArchive->GetFileTimeType(&value)); + + switch(value) + { + case NFileTimeType::kWindows: + case NFileTimeType::kDOS: + case NFileTimeType::kUnix: + fileTimeType = NFileTimeType::EEnum(value); + break; + default: + return E_FAIL; + } + + CObjectVector updatePairs; + GetUpdatePairInfoList(dirItems, archiveItems, fileTimeType, updatePairs); // must be done only once!!! + + CObjectVector updatePairs2; + UpdateProduce(updatePairs, actionSet, updatePairs2); + + UInt32 numFiles = 0; + for (int i = 0; i < updatePairs2.Size(); i++) + if (updatePairs2[i].NewData) + numFiles++; + + RINOK(callback->SetNumFiles(numFiles)); + + + CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; + CMyComPtr updateCallback(updateCallbackSpec); + + updateCallbackSpec->ShareForWrite = shareForWrite; + updateCallbackSpec->StdInMode = stdInMode; + updateCallbackSpec->Callback = callback; + updateCallbackSpec->DirItems = &dirItems; + updateCallbackSpec->ArchiveItems = &archiveItems; + updateCallbackSpec->UpdatePairs = &updatePairs2; + + CMyComPtr outStream; + + const UString &archiveName = archivePath.GetFinalPath(); + if (!stdOutMode) + { + UString resultPath; + int pos; + if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos)) + throw 1417161; + NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos)); + } + + COutFileStream *outStreamSpec = NULL; + COutMultiVolStream *volStreamSpec = NULL; + + if (volumesSizes.Size() == 0) + { + if (stdOutMode) + outStream = new CStdOutFileStream; + else + { + outStreamSpec = new COutFileStream; + outStream = outStreamSpec; + bool isOK = false; + UString realPath; + for (int i = 0; i < (1 << 16); i++) + { + if (archivePath.Temp) + { + if (i > 0) + { + wchar_t s[32]; + ConvertUInt64ToString(i, s); + archivePath.TempPostfix = s; + } + realPath = archivePath.GetTempPath(); + } + else + realPath = archivePath.GetFinalPath(); + if (outStreamSpec->Create(realPath, false)) + { + tempFiles.Paths.Add(realPath); + isOK = true; + break; + } + if (::GetLastError() != ERROR_FILE_EXISTS) + break; + if (!archivePath.Temp) + break; + } + if (!isOK) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.FileName = realPath; + errorInfo.Message = L"Can not open file"; + return E_FAIL; + } + } + } + else + { + if (stdOutMode) + return E_FAIL; + volStreamSpec = new COutMultiVolStream; + outStream = volStreamSpec; + volStreamSpec->Sizes = volumesSizes; + volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L"."); + volStreamSpec->TempFiles = &tempFiles; + volStreamSpec->Init(); + + /* + updateCallbackSpec->VolumesSizes = volumesSizes; + updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; + if (!archivePath.VolExtension.IsEmpty()) + updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension; + */ + } + + RINOK(SetProperties(outArchive, compressionMethod.Properties)); + + if (sfxMode) + { + CInFileStream *sfxStreamSpec = new CInFileStream; + CMyComPtr sfxStream(sfxStreamSpec); + if (!sfxStreamSpec->Open(sfxModule)) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.Message = L"Can't open sfx module"; + errorInfo.FileName = sfxModule; + return E_FAIL; + } + + CMyComPtr sfxOutStream; + COutFileStream *outStreamSpec = NULL; + if (volumesSizes.Size() == 0) + sfxOutStream = outStream; + else + { + outStreamSpec = new COutFileStream; + sfxOutStream = outStreamSpec; + UString realPath = archivePath.GetFinalPath(); + if (!outStreamSpec->Create(realPath, false)) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.FileName = realPath; + errorInfo.Message = L"Can not open file"; + return E_FAIL; + } + } + RINOK(CopyBlock(sfxStream, sfxOutStream)); + if (outStreamSpec) + { + RINOK(outStreamSpec->Close()); + } + } + + HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback); + callback->Finilize(); + RINOK(result); + if (outStreamSpec) + result = outStreamSpec->Close(); + else if (volStreamSpec) + result = volStreamSpec->Close(); + return result; +} + +HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, + IInArchive *archive, + const UString &defaultItemName, + const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo, + CObjectVector &archiveItems) +{ + archiveItems.Clear(); + UInt32 numItems; + RINOK(archive->GetNumberOfItems(&numItems)); + archiveItems.Reserve(numItems); + for(UInt32 i = 0; i < numItems; i++) + { + CArchiveItem ai; + + RINOK(GetArchiveItemPath(archive, i, ai.Name)); + RINOK(IsArchiveItemFolder(archive, i, ai.IsDirectory)); + ai.Censored = censor.CheckPath(ai.Name.IsEmpty() ? defaultItemName : ai.Name, !ai.IsDirectory); + RINOK(GetArchiveItemFileTime(archive, i, + archiveFileInfo.LastWriteTime, ai.LastWriteTime)); + + CPropVariant propertySize; + RINOK(archive->GetProperty(i, kpidSize, &propertySize)); + ai.SizeIsDefined = (propertySize.vt != VT_EMPTY); + if (ai.SizeIsDefined) + ai.Size = ConvertPropVariantToUInt64(propertySize); + + ai.IndexInServer = i; + archiveItems.Add(ai); + } + return S_OK; +} + + +static HRESULT UpdateWithItemLists( + CCodecs *codecs, + CUpdateOptions &options, + IInArchive *archive, + const CObjectVector &archiveItems, + const CObjectVector &dirItems, + CTempFiles &tempFiles, + CUpdateErrorInfo &errorInfo, + IUpdateCallbackUI2 *callback) +{ + for(int i = 0; i < options.Commands.Size(); i++) + { + CUpdateArchiveCommand &command = options.Commands[i]; + if (options.StdOutMode) + { + RINOK(callback->StartArchive(0, archive != 0)); + } + else + { + RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(), + i == 0 && options.UpdateArchiveItself && archive != 0)); + } + + RINOK(Compress( + codecs, + command.ActionSet, archive, + options.MethodMode, + command.ArchivePath, + archiveItems, + options.OpenShareForWrite, + options.StdInMode, + /* options.StdInFileName, */ + options.StdOutMode, + dirItems, + options.SfxMode, options.SfxModule, + options.VolumesSizes, + tempFiles, + errorInfo, callback)); + + RINOK(callback->FinishArchive()); + } + return S_OK; +} + +#ifdef _WIN32 +class CCurrentDirRestorer +{ + UString m_CurrentDirectory; +public: + CCurrentDirRestorer() + { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); } + ~CCurrentDirRestorer() + { RestoreDirectory();} + bool RestoreDirectory() + { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); } +}; +#endif + +struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback +{ + IUpdateCallbackUI2 *Callback; + HRESULT CheckBreak() { return Callback->CheckBreak(); } +}; + +HRESULT UpdateArchive( + CCodecs *codecs, + const NWildcard::CCensor &censor, + CUpdateOptions &options, + CUpdateErrorInfo &errorInfo, + IOpenCallbackUI *openCallback, + IUpdateCallbackUI2 *callback) +{ + if (options.StdOutMode && options.EMailMode) + return E_FAIL; + + if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode)) + return E_NOTIMPL; + + if (options.SfxMode) + { + CProperty property; + property.Name = L"rsfx"; + property.Value = L"on"; + options.MethodMode.Properties.Add(property); + if (options.SfxModule.IsEmpty()) + { + errorInfo.Message = L"sfx file is not specified"; + return E_FAIL; + } + UString name = options.SfxModule; + if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule)) + { + errorInfo.Message = L"can't find specified sfx module"; + return E_FAIL; + } + } + + const UString archiveName = options.ArchivePath.GetFinalPath(); + + UString defaultItemName; + NFind::CFileInfoW archiveFileInfo; + + CArchiveLink archiveLink; + IInArchive *archive = 0; + if (NFind::FindFile(archiveName, archiveFileInfo)) + { + if (archiveFileInfo.IsDirectory()) + throw "there is no such archive"; + if (options.VolumesSizes.Size() > 0) + return E_NOTIMPL; + HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, openCallback); + RINOK(callback->OpenResult(archiveName, result)); + RINOK(result); + if (archiveLink.VolumePaths.Size() > 1) + { + errorInfo.SystemError = (DWORD)E_NOTIMPL; + errorInfo.Message = L"Updating for multivolume archives is not implemented"; + return E_NOTIMPL; + } + archive = archiveLink.GetArchive(); + defaultItemName = archiveLink.GetDefaultItemName(); + } + else + { + /* + if (archiveType.IsEmpty()) + throw "type of archive is not specified"; + */ + } + + CObjectVector dirItems; + if (options.StdInMode) + { + CDirItem item; + item.FullPath = item.Name = options.StdInFileName; + item.Size = (UInt64)(Int64)-1; + item.Attributes = 0; + SYSTEMTIME st; + FILETIME ft; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + item.CreationTime = item.LastAccessTime = item.LastWriteTime = ft; + dirItems.Add(item); + } + else + { + bool needScanning = false; + for(int i = 0; i < options.Commands.Size(); i++) + if (options.Commands[i].ActionSet.NeedScanning()) + needScanning = true; + if (needScanning) + { + CEnumDirItemUpdateCallback enumCallback; + enumCallback.Callback = callback; + RINOK(callback->StartScanning()); + UStringVector errorPaths; + CRecordVector errorCodes; + HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes); + for (int i = 0; i < errorPaths.Size(); i++) + { + RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i])); + } + if(res != S_OK) + { + errorInfo.Message = L"Scanning error"; + // errorInfo.FileName = errorPath; + return res; + } + RINOK(callback->FinishScanning()); + } + } + + UString tempDirPrefix; + bool usesTempDir = false; + + #ifdef _WIN32 + NDirectory::CTempDirectoryW tempDirectory; + if (options.EMailMode && options.EMailRemoveAfter) + { + tempDirectory.Create(kTempFolderPrefix); + tempDirPrefix = tempDirectory.GetPath(); + NormalizeDirPathPrefix(tempDirPrefix); + usesTempDir = true; + } + #endif + + CTempFiles tempFiles; + + bool createTempFile = false; + if(!options.StdOutMode && options.UpdateArchiveItself) + { + CArchivePath &ap = options.Commands[0].ArchivePath; + ap = options.ArchivePath; + // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) + if ((archive != 0 || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) + { + createTempFile = true; + ap.Temp = true; + if (!options.WorkingDir.IsEmpty()) + { + ap.TempPrefix = options.WorkingDir; + NormalizeDirPathPrefix(ap.TempPrefix); + } + } + } + + for(int i = 0; i < options.Commands.Size(); i++) + { + CArchivePath &ap = options.Commands[i].ArchivePath; + if (usesTempDir) + { + // Check it + ap.Prefix = tempDirPrefix; + // ap.Temp = true; + // ap.TempPrefix = tempDirPrefix; + } + if (i > 0 || !createTempFile) + { + const UString &path = ap.GetFinalPath(); + if (NFind::DoesFileExist(path)) + { + errorInfo.SystemError = 0; + errorInfo.Message = L"File already exists"; + errorInfo.FileName = path; + return E_FAIL; + } + } + } + + CObjectVector archiveItems; + if (archive != NULL) + { + RINOK(EnumerateInArchiveItems(censor, + archive, defaultItemName, archiveFileInfo, archiveItems)); + } + + RINOK(UpdateWithItemLists(codecs, options, archive, archiveItems, dirItems, + tempFiles, errorInfo, callback)); + + if (archive != NULL) + { + RINOK(archiveLink.Close()); + archiveLink.Release(); + } + + tempFiles.Paths.Clear(); + if(createTempFile) + { + try + { + CArchivePath &ap = options.Commands[0].ArchivePath; + const UString &tempPath = ap.GetTempPath(); + if (archive != NULL) + if (!NDirectory::DeleteFileAlways(archiveName)) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.Message = L"delete file error"; + errorInfo.FileName = archiveName; + return E_FAIL; + } + if (!NDirectory::MyMoveFile(tempPath, archiveName)) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.Message = L"move file error"; + errorInfo.FileName = tempPath; + errorInfo.FileName2 = archiveName; + return E_FAIL; + } + } + catch(...) + { + throw; + } + } + + #ifdef _WIN32 + if (options.EMailMode) + { + NDLL::CLibrary mapiLib; + if (!mapiLib.Load(TEXT("Mapi32.dll"))) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.Message = L"can not load Mapi32.dll"; + return E_FAIL; + } + LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS) + mapiLib.GetProcAddress("MAPISendDocuments"); + if (fnSend == 0) + { + errorInfo.SystemError = ::GetLastError(); + errorInfo.Message = L"can not find MAPISendDocuments function"; + return E_FAIL; + } + UStringVector fullPaths; + int i; + for(i = 0; i < options.Commands.Size(); i++) + { + CArchivePath &ap = options.Commands[i].ArchivePath; + UString arcPath; + if(!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath)) + { + errorInfo.SystemError = ::GetLastError(); + return E_FAIL; + } + fullPaths.Add(arcPath); + } + CCurrentDirRestorer curDirRestorer; + for(i = 0; i < fullPaths.Size(); i++) + { + UString arcPath = fullPaths[i]; + UString fileName = ExtractFileNameFromPath(arcPath); + AString path = GetAnsiString(arcPath); + AString name = GetAnsiString(fileName); + // Warning!!! MAPISendDocuments function changes Current directory + fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); + } + } + #endif + return S_OK; +} + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.h new file mode 100644 index 0000000..49e4be8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/Update.h @@ -0,0 +1,165 @@ +// Update.h + +#ifndef __UPDATE_H +#define __UPDATE_H + +#include "Common/Wildcard.h" +#include "Windows/FileFind.h" +#include "../../Archive/IArchive.h" + +#include "UpdateAction.h" +#include "ArchiveOpenCallback.h" +#include "UpdateCallback.h" +#include "Property.h" +#include "LoadCodecs.h" + +struct CArchivePath +{ + UString Prefix; // path(folder) prefix including slash + UString Name; // base name + UString BaseExtension; // archive type extension or "exe" extension + UString VolExtension; // archive type extension for volumes + + bool Temp; + UString TempPrefix; // path(folder) for temp location + UString TempPostfix; + + CArchivePath(): Temp(false) {}; + + void ParseFromPath(const UString &path) + { + SplitPathToParts(path, Prefix, Name); + if (Name.IsEmpty()) + return; + int dotPos = Name.ReverseFind(L'.'); + if (dotPos <= 0) + return; + if (dotPos == Name.Length() - 1) + { + Name = Name.Left(dotPos); + BaseExtension.Empty(); + return; + } + if (BaseExtension.CompareNoCase(Name.Mid(dotPos + 1)) == 0) + { + BaseExtension = Name.Mid(dotPos + 1); + Name = Name.Left(dotPos); + } + else + BaseExtension.Empty(); + } + + UString GetPathWithoutExt() const + { + return Prefix + Name; + } + + UString GetFinalPath() const + { + UString path = GetPathWithoutExt(); + if (!BaseExtension.IsEmpty()) + path += UString(L'.') + BaseExtension; + return path; + } + + + UString GetTempPath() const + { + UString path = TempPrefix + Name; + if (!BaseExtension.IsEmpty()) + path += UString(L'.') + BaseExtension; + path += L".tmp"; + path += TempPostfix; + return path; + } +}; + +struct CUpdateArchiveCommand +{ + UString UserArchivePath; + CArchivePath ArchivePath; + NUpdateArchive::CActionSet ActionSet; +}; + +struct CCompressionMethodMode +{ + int FormatIndex; + CObjectVector Properties; + CCompressionMethodMode(): FormatIndex(-1) {} +}; + +struct CUpdateOptions +{ + CCompressionMethodMode MethodMode; + + CObjectVector Commands; + bool UpdateArchiveItself; + CArchivePath ArchivePath; + + bool SfxMode; + UString SfxModule; + + bool OpenShareForWrite; + + bool StdInMode; + UString StdInFileName; + bool StdOutMode; + + bool EMailMode; + bool EMailRemoveAfter; + UString EMailAddress; + + UString WorkingDir; + + bool Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType); + + CUpdateOptions(): + UpdateArchiveItself(true), + SfxMode(false), + StdInMode(false), + StdOutMode(false), + EMailMode(false), + EMailRemoveAfter(false), + OpenShareForWrite(false) + {}; + CRecordVector VolumesSizes; +}; + +struct CErrorInfo +{ + DWORD SystemError; + UString FileName; + UString FileName2; + UString Message; + // UStringVector ErrorPaths; + // CRecordVector ErrorCodes; + CErrorInfo(): SystemError(0) {}; +}; + +struct CUpdateErrorInfo: public CErrorInfo +{ +}; + +#define INTERFACE_IUpdateCallbackUI2(x) \ + INTERFACE_IUpdateCallbackUI(x) \ + virtual HRESULT OpenResult(const wchar_t *name, HRESULT result) x; \ + virtual HRESULT StartScanning() x; \ + virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ + virtual HRESULT FinishScanning() x; \ + virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \ + virtual HRESULT FinishArchive() x; \ + +struct IUpdateCallbackUI2: public IUpdateCallbackUI +{ + INTERFACE_IUpdateCallbackUI2(=0) +}; + +HRESULT UpdateArchive( + CCodecs *codecs, + const NWildcard::CCensor &censor, + CUpdateOptions &options, + CUpdateErrorInfo &errorInfo, + IOpenCallbackUI *openCallback, + IUpdateCallbackUI2 *callback); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.cpp new file mode 100644 index 0000000..5e3b5a1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.cpp @@ -0,0 +1,64 @@ +// UpdateAction.cpp + +#include "StdAfx.h" + +#include "UpdateAction.h" + +namespace NUpdateArchive { + +const CActionSet kAddActionSet = +{ + NPairAction::kCopy, + NPairAction::kCopy, + NPairAction::kCompress, + NPairAction::kCompress, + NPairAction::kCompress, + NPairAction::kCompress, + NPairAction::kCompress +}; + +const CActionSet kUpdateActionSet = +{ + NPairAction::kCopy, + NPairAction::kCopy, + NPairAction::kCompress, + NPairAction::kCopy, + NPairAction::kCompress, + NPairAction::kCopy, + NPairAction::kCompress +}; + +const CActionSet kFreshActionSet = +{ + NPairAction::kCopy, + NPairAction::kCopy, + NPairAction::kIgnore, + NPairAction::kCopy, + NPairAction::kCompress, + NPairAction::kCopy, + NPairAction::kCompress +}; + +const CActionSet kSynchronizeActionSet = +{ + NPairAction::kCopy, + NPairAction::kIgnore, + NPairAction::kCompress, + NPairAction::kCopy, + NPairAction::kCompress, + NPairAction::kCopy, + NPairAction::kCompress, +}; + +const CActionSet kDeleteActionSet = +{ + NPairAction::kCopy, + NPairAction::kIgnore, + NPairAction::kIgnore, + NPairAction::kIgnore, + NPairAction::kIgnore, + NPairAction::kIgnore, + NPairAction::kIgnore +}; + +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.h new file mode 100644 index 0000000..aa05097 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateAction.h @@ -0,0 +1,57 @@ +// UpdateAction.h + +#ifndef __UPDATE_ACTION_H +#define __UPDATE_ACTION_H + +namespace NUpdateArchive { + + namespace NPairState + { + const int kNumValues = 7; + enum EEnum + { + kNotMasked = 0, + kOnlyInArchive, + kOnlyOnDisk, + kNewInArchive, + kOldInArchive, + kSameFiles, + kUnknowNewerFiles + }; + } + namespace NPairAction + { + enum EEnum + { + kIgnore = 0, + kCopy, + kCompress, + kCompressAsAnti + }; + } + struct CActionSet + { + NPairAction::EEnum StateActions[NPairState::kNumValues]; + bool NeedScanning() const + { + int i; + for (i = 0; i < NPairState::kNumValues; i++) + if (StateActions[i] == NPairAction::kCompress) + return true; + for (i = 1; i < NPairState::kNumValues; i++) + if (StateActions[i] != NPairAction::kIgnore) + return true; + return false; + } + }; + extern const CActionSet kAddActionSet; + extern const CActionSet kUpdateActionSet; + extern const CActionSet kFreshActionSet; + extern const CActionSet kSynchronizeActionSet; + extern const CActionSet kDeleteActionSet; +}; + + +#endif + + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp new file mode 100644 index 0000000..a5f0a54 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp @@ -0,0 +1,267 @@ +// UpdateCallback.cpp + +#include "StdAfx.h" + +#include "UpdateCallback.h" + +#include "Common/StringConvert.h" +#include "Common/IntToString.h" +#include "Common/Defs.h" +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" + +#include "../../Common/FileStreams.h" + +using namespace NWindows; + +CArchiveUpdateCallback::CArchiveUpdateCallback(): + Callback(0), + ShareForWrite(false), + StdInMode(false), + DirItems(0), + ArchiveItems(0), + UpdatePairs(0) + {} + + +STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) +{ + COM_TRY_BEGIN + return Callback->SetTotal(size); + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) +{ + COM_TRY_BEGIN + return Callback->SetCompleted(completeValue); + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + COM_TRY_BEGIN + return Callback->SetRatioInfo(inSize, outSize); + COM_TRY_END +} + + +/* +STATPROPSTG kProperties[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsFolder, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidLastAccessTime, VT_FILETIME}, + { NULL, kpidCreationTime, VT_FILETIME}, + { NULL, kpidLastWriteTime, VT_FILETIME}, + { NULL, kpidAttributes, VT_UI4}, + { NULL, kpidIsAnti, VT_BOOL} +}; +*/ + +STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) +{ + return E_NOTIMPL; + /* + return CStatPropEnumerator::CreateEnumerator(kProperties, + sizeof(kProperties) / sizeof(kProperties[0]), enumerator); + */ +} + +STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, + Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive) +{ + COM_TRY_BEGIN + RINOK(Callback->CheckBreak()); + const CUpdatePair2 &updatePair = (*UpdatePairs)[index]; + if(newData != NULL) + *newData = BoolToInt(updatePair.NewData); + if(newProperties != NULL) + *newProperties = BoolToInt(updatePair.NewProperties); + if(indexInArchive != NULL) + { + if (updatePair.ExistInArchive) + { + if (ArchiveItems == 0) + *indexInArchive = updatePair.ArchiveItemIndex; + else + *indexInArchive = (*ArchiveItems)[updatePair.ArchiveItemIndex].IndexInServer; + } + else + *indexInArchive = UInt32(-1); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + const CUpdatePair2 &updatePair = (*UpdatePairs)[index]; + NWindows::NCOM::CPropVariant propVariant; + + if (propID == kpidIsAnti) + { + propVariant = updatePair.IsAnti; + propVariant.Detach(value); + return S_OK; + } + + if (updatePair.IsAnti) + { + switch(propID) + { + case kpidIsFolder: + case kpidPath: + break; + case kpidSize: + propVariant = (UInt64)0; + propVariant.Detach(value); + return S_OK; + default: + propVariant.Detach(value); + return S_OK; + } + } + + if(updatePair.ExistOnDisk) + { + const CDirItem &dirItem = (*DirItems)[updatePair.DirItemIndex]; + switch(propID) + { + case kpidPath: + propVariant = dirItem.Name; + break; + case kpidIsFolder: + propVariant = dirItem.IsDirectory(); + break; + case kpidSize: + propVariant = dirItem.Size; + break; + case kpidAttributes: + propVariant = dirItem.Attributes; + break; + case kpidLastAccessTime: + propVariant = dirItem.LastAccessTime; + break; + case kpidCreationTime: + propVariant = dirItem.CreationTime; + break; + case kpidLastWriteTime: + propVariant = dirItem.LastWriteTime; + break; + } + } + else + { + if (propID == kpidPath) + { + if (updatePair.NewNameIsDefined) + { + propVariant = updatePair.NewName; + propVariant.Detach(value); + return S_OK; + } + } + if (updatePair.ExistInArchive && Archive) + { + UInt32 indexInArchive; + if (ArchiveItems == 0) + indexInArchive = updatePair.ArchiveItemIndex; + else + indexInArchive = (*ArchiveItems)[updatePair.ArchiveItemIndex].IndexInServer; + return Archive->GetProperty(indexInArchive, propID, value); + } + } + propVariant.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) +{ + COM_TRY_BEGIN + const CUpdatePair2 &updatePair = (*UpdatePairs)[index]; + if(!updatePair.NewData) + return E_FAIL; + + RINOK(Callback->CheckBreak()); + RINOK(Callback->Finilize()); + + if(updatePair.IsAnti) + { + return Callback->GetStream((*ArchiveItems)[updatePair.ArchiveItemIndex].Name, true); + } + const CDirItem &dirItem = (*DirItems)[updatePair.DirItemIndex]; + RINOK(Callback->GetStream(dirItem.Name, false)); + + if(dirItem.IsDirectory()) + return S_OK; + + if (StdInMode) + { + CStdInFileStream *inStreamSpec = new CStdInFileStream; + CMyComPtr inStreamLoc(inStreamSpec); + *inStream = inStreamLoc.Detach(); + } + else + { + CInFileStream *inStreamSpec = new CInFileStream; + CMyComPtr inStreamLoc(inStreamSpec); + UString path = DirPrefix + dirItem.FullPath; + if(!inStreamSpec->OpenShared(path, ShareForWrite)) + { + return Callback->OpenFileError(path, ::GetLastError()); + } + *inStream = inStreamLoc.Detach(); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult) +{ + COM_TRY_BEGIN + return Callback->SetOperationResult(operationResult); + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) +{ + if (VolumesSizes.Size() == 0) + return S_FALSE; + if (index >= (UInt32)VolumesSizes.Size()) + index = VolumesSizes.Size() - 1; + *size = VolumesSizes[index]; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) +{ + COM_TRY_BEGIN + wchar_t temp[32]; + ConvertUInt64ToString(index + 1, temp); + UString res = temp; + while (res.Length() < 2) + res = UString(L'0') + res; + UString fileName = VolName; + fileName += L'.'; + fileName += res; + fileName += VolExt; + COutFileStream *streamSpec = new COutFileStream; + CMyComPtr streamLoc(streamSpec); + if(!streamSpec->Create(fileName, false)) + return ::GetLastError(); + *volumeStream = streamLoc.Detach(); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) +{ + COM_TRY_BEGIN + return Callback->CryptoGetTextPassword2(passwordIsDefined, password); + COM_TRY_END +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.h new file mode 100644 index 0000000..bf90ff9 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateCallback.h @@ -0,0 +1,82 @@ +// UpdateCallback.h + +#ifndef __UPDATECALLBACK_H +#define __UPDATECALLBACK_H + +#include "Common/MyCom.h" +#include "Common/MyString.h" + +#include "../../IPassword.h" +#include "../../ICoder.h" + +#include "../Common/UpdatePair.h" +#include "../Common/UpdateProduce.h" + +#define INTERFACE_IUpdateCallbackUI(x) \ + virtual HRESULT SetTotal(UInt64 size) x; \ + virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ + virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \ + virtual HRESULT CheckBreak() x; \ + virtual HRESULT Finilize() x; \ + virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ + virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \ + virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ + virtual HRESULT SetOperationResult(Int32 operationResult) x; \ + virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \ + + // virtual HRESULT CloseProgress() { return S_OK; }; + +struct IUpdateCallbackUI +{ + INTERFACE_IUpdateCallbackUI(=0) +}; + +class CArchiveUpdateCallback: + public IArchiveUpdateCallback2, + public ICryptoGetTextPassword2, + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP3( + IArchiveUpdateCallback2, + ICryptoGetTextPassword2, + ICompressProgressInfo) + + // IProgress + STDMETHOD(SetTotal)(UInt64 size); + STDMETHOD(SetCompleted)(const UInt64 *completeValue); + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); + + // IUpdateCallback + STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator); + STDMETHOD(GetUpdateItemInfo)(UInt32 index, + Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive); + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream); + STDMETHOD(SetOperationResult)(Int32 operationResult); + + STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size); + STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream); + + STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); + +public: + CRecordVector VolumesSizes; + UString VolName; + UString VolExt; + + IUpdateCallbackUI *Callback; + + UString DirPrefix; + bool ShareForWrite; + bool StdInMode; + const CObjectVector *DirItems; + const CObjectVector *ArchiveItems; + const CObjectVector *UpdatePairs; + CMyComPtr Archive; + + CArchiveUpdateCallback(); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.cpp new file mode 100644 index 0000000..b4fb2a1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.cpp @@ -0,0 +1,166 @@ +// UpdatePair.cpp + +#include "StdAfx.h" + +#include + +#include "Common/Defs.h" +#include "Common/Wildcard.h" +#include "Windows/Time.h" + +#include "UpdatePair.h" +#include "SortUtils.h" + +using namespace NWindows; +using namespace NTime; + +static int MyCompareTime(NFileTimeType::EEnum fileTimeType, + const FILETIME &time1, const FILETIME &time2) +{ + switch(fileTimeType) + { + case NFileTimeType::kWindows: + return ::CompareFileTime(&time1, &time2); + case NFileTimeType::kUnix: + { + UInt32 unixTime1, unixTime2; + if (!FileTimeToUnixTime(time1, unixTime1)) + { + unixTime1 = 0; + // throw 4191614; + } + if (!FileTimeToUnixTime(time2, unixTime2)) + { + unixTime2 = 0; + // throw 4191615; + } + return MyCompare(unixTime1, unixTime2); + } + case NFileTimeType::kDOS: + { + UInt32 dosTime1, dosTime2; + FileTimeToDosTime(time1, dosTime1); + FileTimeToDosTime(time2, dosTime2); + /* + if (!FileTimeToDosTime(time1, dosTime1)) + throw 4191616; + if (!FileTimeToDosTime(time2, dosTime2)) + throw 4191617; + */ + return MyCompare(dosTime1, dosTime2); + } + } + throw 4191618; +} + +static const wchar_t *kDuplicateFileNameMessage = L"Duplicate filename:"; + +/* +static const char *kNotCensoredCollisionMessaged = "Internal file name collision:\n"; +static const char *kSameTimeChangedSizeCollisionMessaged = + "Collision between files with same date/time and different sizes:\n"; +*/ + +static void TestDuplicateString(const UStringVector &strings, const CIntVector &indices) +{ + for(int i = 0; i + 1 < indices.Size(); i++) + if (CompareFileNames(strings[indices[i]], strings[indices[i + 1]]) == 0) + { + UString message = kDuplicateFileNameMessage; + message += L"\n"; + message += strings[indices[i]]; + message += L"\n"; + message += strings[indices[i + 1]]; + throw message; + } +} + +void GetUpdatePairInfoList( + const CObjectVector &dirItems, + const CObjectVector &archiveItems, + NFileTimeType::EEnum fileTimeType, + CObjectVector &updatePairs) +{ + CIntVector dirIndices, archiveIndices; + UStringVector dirNames, archiveNames; + + int numDirItems = dirItems.Size(); + int i; + for(i = 0; i < numDirItems; i++) + dirNames.Add(dirItems[i].Name); + SortFileNames(dirNames, dirIndices); + TestDuplicateString(dirNames, dirIndices); + + int numArchiveItems = archiveItems.Size(); + for(i = 0; i < numArchiveItems; i++) + archiveNames.Add(archiveItems[i].Name); + SortFileNames(archiveNames, archiveIndices); + TestDuplicateString(archiveNames, archiveIndices); + + int dirItemIndex = 0, archiveItemIndex = 0; + CUpdatePair pair; + while(dirItemIndex < numDirItems && archiveItemIndex < numArchiveItems) + { + int dirItemIndex2 = dirIndices[dirItemIndex], + archiveItemIndex2 = archiveIndices[archiveItemIndex]; + const CDirItem &dirItem = dirItems[dirItemIndex2]; + const CArchiveItem &archiveItem = archiveItems[archiveItemIndex2]; + int compareResult = CompareFileNames(dirItem.Name, archiveItem.Name); + if (compareResult < 0) + { + pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; + pair.DirItemIndex = dirItemIndex2; + dirItemIndex++; + } + else if (compareResult > 0) + { + pair.State = archiveItem.Censored ? + NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked; + pair.ArchiveItemIndex = archiveItemIndex2; + archiveItemIndex++; + } + else + { + if (!archiveItem.Censored) + throw 1082022;; // TTString(kNotCensoredCollisionMessaged + dirItem.Name); + pair.DirItemIndex = dirItemIndex2; + pair.ArchiveItemIndex = archiveItemIndex2; + switch (MyCompareTime(fileTimeType, dirItem.LastWriteTime, archiveItem.LastWriteTime)) + { + case -1: + pair.State = NUpdateArchive::NPairState::kNewInArchive; + break; + case 1: + pair.State = NUpdateArchive::NPairState::kOldInArchive; + break; + default: + if (archiveItem.SizeIsDefined) + if (dirItem.Size != archiveItem.Size) + // throw 1082034; // kSameTimeChangedSizeCollisionMessaged; + pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles; + else + pair.State = NUpdateArchive::NPairState::kSameFiles; + else + pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles; + } + dirItemIndex++; + archiveItemIndex++; + } + updatePairs.Add(pair); + } + for(;dirItemIndex < numDirItems; dirItemIndex++) + { + pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; + pair.DirItemIndex = dirIndices[dirItemIndex]; + updatePairs.Add(pair); + } + for(;archiveItemIndex < numArchiveItems; archiveItemIndex++) + { + int archiveItemIndex2 = archiveIndices[archiveItemIndex]; + const CArchiveItem &archiveItem = archiveItems[archiveItemIndex2]; + pair.State = archiveItem.Censored ? + NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked; + pair.ArchiveItemIndex = archiveItemIndex2; + updatePairs.Add(pair); + } +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.h new file mode 100644 index 0000000..f50a23f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdatePair.h @@ -0,0 +1,24 @@ +// UpdatePair.h + +#ifndef __UPDATE_PAIR_H +#define __UPDATE_PAIR_H + +#include "DirItem.h" +#include "UpdateAction.h" + +#include "../../Archive/IArchive.h" + +struct CUpdatePair +{ + NUpdateArchive::NPairState::EEnum State; + int ArchiveItemIndex; + int DirItemIndex; +}; + +void GetUpdatePairInfoList( + const CObjectVector &dirItems, + const CObjectVector &archiveItems, + NFileTimeType::EEnum fileTimeType, + CObjectVector &updatePairs); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp new file mode 100644 index 0000000..5552161 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp @@ -0,0 +1,63 @@ +// UpdateProduce.cpp + +#include "StdAfx.h" + +#include "UpdateProduce.h" + +using namespace NUpdateArchive; + +static const char *kUpdateActionSetCollision = + "Internal collision in update action set"; + +void UpdateProduce( + const CObjectVector &updatePairs, + const NUpdateArchive::CActionSet &actionSet, + CObjectVector &operationChain) +{ + for(int i = 0; i < updatePairs.Size(); i++) + { + // CUpdateArchiveRange aRange; + const CUpdatePair &pair = updatePairs[i]; + + CUpdatePair2 pair2; + pair2.IsAnti = false; + pair2.ArchiveItemIndex = pair.ArchiveItemIndex; + pair2.DirItemIndex = pair.DirItemIndex; + pair2.ExistInArchive = (pair.State != NPairState::kOnlyOnDisk); + pair2.ExistOnDisk = (pair.State != NPairState::kOnlyInArchive && pair.State != NPairState::kNotMasked); + switch(actionSet.StateActions[pair.State]) + { + case NPairAction::kIgnore: + /* + if (pair.State != NPairState::kOnlyOnDisk) + IgnoreArchiveItem(m_ArchiveItems[pair.ArchiveItemIndex]); + // cout << "deleting"; + */ + break; + case NPairAction::kCopy: + { + if (pair.State == NPairState::kOnlyOnDisk) + throw kUpdateActionSetCollision; + pair2.NewData = pair2.NewProperties = false; + operationChain.Add(pair2); + break; + } + case NPairAction::kCompress: + { + if (pair.State == NPairState::kOnlyInArchive || + pair.State == NPairState::kNotMasked) + throw kUpdateActionSetCollision; + pair2.NewData = pair2.NewProperties = true; + operationChain.Add(pair2); + break; + } + case NPairAction::kCompressAsAnti: + { + pair2.IsAnti = true; + pair2.NewData = pair2.NewProperties = true; + operationChain.Add(pair2); + break; + } + } + } +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.h new file mode 100644 index 0000000..8f58dab --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/UpdateProduce.h @@ -0,0 +1,31 @@ +// UpdateProduce.h + +#ifndef __UPDATE_PRODUCE_H +#define __UPDATE_PRODUCE_H + +#include "UpdatePair.h" + +struct CUpdatePair2 +{ + // bool OperationIsCompress; + bool NewData; + bool NewProperties; + + bool ExistInArchive; + bool ExistOnDisk; + bool IsAnti; + int ArchiveItemIndex; + int DirItemIndex; + + bool NewNameIsDefined; + UString NewName; + + CUpdatePair2(): NewNameIsDefined(false) {} +}; + +void UpdateProduce( + const CObjectVector &updatePairs, + const NUpdateArchive::CActionSet &actionSet, + CObjectVector &operationChain); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.cpp new file mode 100644 index 0000000..8db6f4f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.cpp @@ -0,0 +1,64 @@ +// WorkDir.cpp + +#include "StdAfx.h" + +#include "WorkDir.h" + +#include "Common/StringConvert.h" +#include "Common/Wildcard.h" + +#include "Windows/FileName.h" +#include "Windows/FileDir.h" + +static inline UINT GetCurrentCodePage() + { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } + +using namespace NWindows; +using namespace NFile; +using namespace NName; + +UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path) +{ + NWorkDir::NMode::EEnum mode = workDirInfo.Mode; + if (workDirInfo.ForRemovableOnly) + { + mode = NWorkDir::NMode::kCurrent; + UString prefix = path.Left(3); + if (prefix[1] == L':' && prefix[2] == L'\\') + { + UINT driveType = GetDriveType(GetSystemString(prefix, GetCurrentCodePage())); + if (driveType == DRIVE_CDROM || driveType == DRIVE_REMOVABLE) + mode = workDirInfo.Mode; + } + /* + CParsedPath parsedPath; + parsedPath.ParsePath(archiveName); + UINT driveType = GetDriveType(parsedPath.Prefix); + if ((driveType != DRIVE_CDROM) && (driveType != DRIVE_REMOVABLE)) + mode = NZipSettings::NWorkDir::NMode::kCurrent; + */ + } + switch(mode) + { + case NWorkDir::NMode::kCurrent: + { + return ExtractDirPrefixFromPath(path); + } + case NWorkDir::NMode::kSpecified: + { + UString tempDir = workDirInfo.Path; + NormalizeDirPathPrefix(tempDir); + return tempDir; + } + default: + { + UString tempDir; + if(!NFile::NDirectory::MyGetTempPath(tempDir)) + throw 141717; + return tempDir; + } + } +} + + + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.h new file mode 100644 index 0000000..0643d67 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/WorkDir.h @@ -0,0 +1,10 @@ +// WorkDir.h + +#ifndef __WORKDIR_H +#define __WORKDIR_H + +#include "ZipRegistry.h" + +UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ZipRegistry.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ZipRegistry.h new file mode 100644 index 0000000..753287d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Common/ZipRegistry.h @@ -0,0 +1,98 @@ +// ZipRegistry.h + +#ifndef __ZIPREGISTRY_H +#define __ZIPREGISTRY_H + +#include "Common/MyString.h" +#include "Common/Types.h" +#include "ExtractMode.h" + +namespace NExtract +{ + struct CInfo + { + NPathMode::EEnum PathMode; + NOverwriteMode::EEnum OverwriteMode; + UStringVector Paths; + bool ShowPassword; + }; +} + +namespace NCompression { + + struct CFormatOptions + { + CSysString FormatID; + UString Options; + UString Method; + UString EncryptionMethod; + UInt32 Level; + UInt32 Dictionary; + UInt32 Order; + UInt32 BlockLogSize; + UInt32 NumThreads; + void ResetForLevelChange() + { + BlockLogSize = NumThreads = Level = Dictionary = Order = UInt32(-1); + Method.Empty(); + // EncryptionMethod.Empty(); + // Options.Empty(); + } + CFormatOptions() { ResetForLevelChange(); } + }; + + struct CInfo + { + UStringVector HistoryArchives; + UInt32 Level; + UString ArchiveType; + + CObjectVector FormatOptionsVector; + + bool ShowPassword; + bool EncryptHeaders; + }; +} + +namespace NWorkDir{ + + namespace NMode + { + enum EEnum + { + kSystem, + kCurrent, + kSpecified + }; + } + struct CInfo + { + NMode::EEnum Mode; + UString Path; + bool ForRemovableOnly; + void SetForRemovableOnlyDefault() { ForRemovableOnly = true; } + void SetDefault() + { + Mode = NMode::kSystem; + Path.Empty(); + SetForRemovableOnlyDefault(); + } + }; +} + +void SaveExtractionInfo(const NExtract::CInfo &info); +void ReadExtractionInfo(NExtract::CInfo &info); + +void SaveCompressionInfo(const NCompression::CInfo &info); +void ReadCompressionInfo(NCompression::CInfo &info); + +void SaveWorkDirInfo(const NWorkDir::CInfo &info); +void ReadWorkDirInfo(NWorkDir::CInfo &info); + +void SaveCascadedMenu(bool enabled); +bool ReadCascadedMenu(); + +void SaveContextMenuStatus(UInt32 value); +bool ReadContextMenuStatus(UInt32 &value); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp new file mode 100644 index 0000000..d18b39e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp @@ -0,0 +1,63 @@ +// ConsoleClose.cpp + +#include "StdAfx.h" + +#include "ConsoleClose.h" + +static int g_BreakCounter = 0; +static const int kBreakAbortThreshold = 2; + +namespace NConsoleClose { + +static BOOL WINAPI HandlerRoutine(DWORD ctrlType) +{ + if (ctrlType == CTRL_LOGOFF_EVENT) + { + // printf("\nCTRL_LOGOFF_EVENT\n"); + return TRUE; + } + + g_BreakCounter++; + if (g_BreakCounter < kBreakAbortThreshold) + return TRUE; + return FALSE; + /* + switch(ctrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + if (g_BreakCounter < kBreakAbortThreshold) + return TRUE; + } + return FALSE; + */ +} + +bool TestBreakSignal() +{ + /* + if (g_BreakCounter > 0) + return true; + */ + return (g_BreakCounter > 0); +} + +void CheckCtrlBreak() +{ + if (TestBreakSignal()) + throw CCtrlBreakException(); +} + +CCtrlHandlerSetter::CCtrlHandlerSetter() +{ + if(!SetConsoleCtrlHandler(HandlerRoutine, TRUE)) + throw "SetConsoleCtrlHandler fails"; +} + +CCtrlHandlerSetter::~CCtrlHandlerSetter() +{ + if(!SetConsoleCtrlHandler(HandlerRoutine, FALSE)) + throw "SetConsoleCtrlHandler fails"; +} + +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.h new file mode 100644 index 0000000..3c5fd55 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ConsoleClose.h @@ -0,0 +1,24 @@ +// ConsoleCloseUtils.h + +#ifndef __CONSOLECLOSEUTILS_H +#define __CONSOLECLOSEUTILS_H + +namespace NConsoleClose { + +bool TestBreakSignal(); + +class CCtrlHandlerSetter +{ +public: + CCtrlHandlerSetter(); + virtual ~CCtrlHandlerSetter(); +}; + +class CCtrlBreakException +{}; + +void CheckCtrlBreak(); + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp new file mode 100644 index 0000000..d693cb4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp @@ -0,0 +1,235 @@ +// ExtractCallbackConsole.h + +#include "StdAfx.h" + +#include "ExtractCallbackConsole.h" +#include "UserInputUtils.h" +#include "ConsoleClose.h" + +#include "Common/Wildcard.h" + +#include "Windows/FileDir.h" +#include "Windows/FileFind.h" +#include "Windows/Time.h" +#include "Windows/Defs.h" +#include "Windows/PropVariant.h" +#include "Windows/Error.h" +#include "Windows/PropVariantConversions.h" + +#include "../../Common/FilePathAutoRename.h" + +#include "../Common/ExtractingFilePath.h" + +using namespace NWindows; +using namespace NFile; +using namespace NDirectory; + +static const char *kTestingString = "Testing "; +static const char *kExtractingString = "Extracting "; +static const char *kSkippingString = "Skipping "; + +// static const char *kCantAutoRename = "can not create file with auto name\n"; +// static const char *kCantRenameFile = "can not rename existing file\n"; +// static const char *kCantDeleteOutputFile = "can not delete output file "; +static const char *kError = "ERROR: "; +static const char *kMemoryExceptionMessage = "Can't allocate required memory!"; + +static const char *kProcessing = "Processing archive: "; +static const char *kEverythingIsOk = "Everything is Ok"; +static const char *kNoFiles = "No files to process"; + +static const char *kUnsupportedMethod = "Unsupported Method"; +static const char *kCrcFailed = "CRC Failed"; +static const char *kCrcFailedEncrypted = "CRC Failed in encrypted file. Wrong password?"; +static const char *kDataError = "Data Error"; +static const char *kDataErrorEncrypted = "Data Error in encrypted file. Wrong password?"; +static const char *kUnknownError = "Unknown Error"; + +STDMETHODIMP CExtractCallbackConsole::SetTotal(UInt64) +{ + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::SetCompleted(const UInt64 *) +{ + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::AskOverwrite( + const wchar_t *existName, const FILETIME *, const UInt64 *, + const wchar_t *newName, const FILETIME *, const UInt64 *, + Int32 *answer) +{ + (*OutStream) << "file " << existName << + "\nalready exists. Overwrite with " << endl; + (*OutStream) << newName; + + NUserAnswerMode::EEnum overwriteAnswer = ScanUserYesNoAllQuit(OutStream); + + switch(overwriteAnswer) + { + case NUserAnswerMode::kQuit: + return E_ABORT; + case NUserAnswerMode::kNo: + *answer = NOverwriteAnswer::kNo; + break; + case NUserAnswerMode::kNoAll: + *answer = NOverwriteAnswer::kNoToAll; + break; + case NUserAnswerMode::kYesAll: + *answer = NOverwriteAnswer::kYesToAll; + break; + case NUserAnswerMode::kYes: + *answer = NOverwriteAnswer::kYes; + break; + case NUserAnswerMode::kAutoRename: + *answer = NOverwriteAnswer::kAutoRename; + break; + default: + return E_FAIL; + } + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::PrepareOperation(const wchar_t *name, bool /* isFolder */, Int32 askExtractMode, const UInt64 *position) +{ + switch (askExtractMode) + { + case NArchive::NExtract::NAskMode::kExtract: + (*OutStream) << kExtractingString; + break; + case NArchive::NExtract::NAskMode::kTest: + (*OutStream) << kTestingString; + break; + case NArchive::NExtract::NAskMode::kSkip: + (*OutStream) << kSkippingString; + break; + }; + (*OutStream) << name; + if (position != 0) + (*OutStream) << " <" << *position << ">"; + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::MessageError(const wchar_t *message) +{ + (*OutStream) << message << endl; + NumFileErrorsInCurrentArchive++; + NumFileErrors++; + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::SetOperationResult(Int32 operationResult, bool encrypted) +{ + switch(operationResult) + { + case NArchive::NExtract::NOperationResult::kOK: + break; + default: + { + NumFileErrorsInCurrentArchive++; + NumFileErrors++; + (*OutStream) << " "; + switch(operationResult) + { + case NArchive::NExtract::NOperationResult::kUnSupportedMethod: + (*OutStream) << kUnsupportedMethod; + break; + case NArchive::NExtract::NOperationResult::kCRCError: + (*OutStream) << (encrypted ? kCrcFailedEncrypted: kCrcFailed); + break; + case NArchive::NExtract::NOperationResult::kDataError: + (*OutStream) << (encrypted ? kDataErrorEncrypted : kDataError); + break; + default: + (*OutStream) << kUnknownError; + } + } + } + (*OutStream) << endl; + return S_OK; +} + +STDMETHODIMP CExtractCallbackConsole::CryptoGetTextPassword(BSTR *password) +{ + if (!PasswordIsDefined) + { + Password = GetPassword(OutStream); + PasswordIsDefined = true; + } + CMyComBSTR tempName(Password); + *password = tempName.Detach(); + return S_OK; +} + +HRESULT CExtractCallbackConsole::BeforeOpen(const wchar_t *name) +{ + NumArchives++; + NumFileErrorsInCurrentArchive = 0; + (*OutStream) << endl << kProcessing << name << endl; + return S_OK; +} + +HRESULT CExtractCallbackConsole::OpenResult(const wchar_t * /* name */, HRESULT result, bool encrypted) +{ + (*OutStream) << endl; + if (result != S_OK) + { + (*OutStream) << "Error: "; + if (encrypted) + (*OutStream) << "Can not open encrypted archive. Wrong password?"; + else + (*OutStream) << "Can not open file as archive"; + (*OutStream) << endl; + NumArchiveErrors++; + } + return S_OK; +} + +HRESULT CExtractCallbackConsole::ThereAreNoFiles() +{ + (*OutStream) << endl << kNoFiles << endl; + return S_OK; +} + +HRESULT CExtractCallbackConsole::ExtractResult(HRESULT result) +{ + if (result == S_OK) + { + (*OutStream) << endl; + if (NumFileErrorsInCurrentArchive == 0) + (*OutStream) << kEverythingIsOk << endl; + else + { + NumArchiveErrors++; + (*OutStream) << "Sub items Errors: " << NumFileErrorsInCurrentArchive << endl; + } + } + if (result == S_OK) + return result; + NumArchiveErrors++; + if (result == E_ABORT || result == ERROR_DISK_FULL) + return result; + (*OutStream) << endl << kError; + if (result == E_OUTOFMEMORY) + (*OutStream) << kMemoryExceptionMessage; + else + { + UString message; + NError::MyFormatMessage(result, message); + (*OutStream) << message; + } + (*OutStream) << endl; + return S_OK; +} + +HRESULT CExtractCallbackConsole::SetPassword(const UString &password) +{ + PasswordIsDefined = true; + Password = password; + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h new file mode 100644 index 0000000..7e5d9c5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h @@ -0,0 +1,65 @@ +// ExtractCallbackConsole.h + +#ifndef __EXTRACTCALLBACKCONSOLE_H +#define __EXTRACTCALLBACKCONSOLE_H + +#include "Common/MyString.h" +#include "Common/StdOutStream.h" +#include "../../Common/FileStreams.h" +#include "../../IPassword.h" +#include "../../Archive/IArchive.h" +#include "../Common/ArchiveExtractCallback.h" + +class CExtractCallbackConsole: + public IExtractCallbackUI, + public ICryptoGetTextPassword, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP2(IFolderArchiveExtractCallback, ICryptoGetTextPassword) + + STDMETHOD(SetTotal)(UInt64 total); + STDMETHOD(SetCompleted)(const UInt64 *completeValue); + + // IFolderArchiveExtractCallback + STDMETHOD(AskOverwrite)( + const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, + const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, + Int32 *answer); + STDMETHOD (PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position); + + STDMETHOD(MessageError)(const wchar_t *message); + STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted); + + // ICryptoGetTextPassword + STDMETHOD(CryptoGetTextPassword)(BSTR *password); + + HRESULT BeforeOpen(const wchar_t *name); + HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted); + HRESULT ThereAreNoFiles(); + HRESULT ExtractResult(HRESULT result); + + HRESULT SetPassword(const UString &password); + +public: + bool PasswordIsDefined; + UString Password; + + UInt64 NumArchives; + UInt64 NumArchiveErrors; + UInt64 NumFileErrors; + UInt64 NumFileErrorsInCurrentArchive; + + CStdOutStream *OutStream; + + void Init() + { + NumArchives = 0; + NumArchiveErrors = 0; + NumFileErrors = 0; + NumFileErrorsInCurrentArchive = 0; + } + +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.cpp new file mode 100644 index 0000000..7a2b962 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.cpp @@ -0,0 +1,579 @@ +// List.cpp + +#include "StdAfx.h" + +#include "List.h" +#include "ConsoleClose.h" + +#include "Common/StringConvert.h" +#include "Common/StdOutStream.h" +#include "Common/IntToString.h" +#include "Common/MyCom.h" + +#include "Windows/PropVariant.h" +#include "Windows/Defs.h" +#include "Windows/PropVariantConversions.h" +#include "Windows/FileDir.h" + +#include "../../Archive/IArchive.h" + +#include "../Common/PropIDUtils.h" +#include "../Common/OpenArchive.h" + +#include "OpenCallbackConsole.h" + +using namespace NWindows; + +struct CPropIdToName +{ + PROPID PropID; + const wchar_t *Name; +}; + +static CPropIdToName kPropIdToName[] = +{ + { kpidPath, L"Path" }, + { kpidName, L"Name" }, + { kpidIsFolder, L"Folder" }, + { kpidSize, L"Size" }, + { kpidPackedSize, L"Packed Size" }, + { kpidAttributes, L"Attributes" }, + { kpidCreationTime, L"Created" }, + { kpidLastAccessTime, L"Accessed" }, + { kpidLastWriteTime, L"Modified" }, + { kpidSolid, L"Solid" }, + { kpidCommented, L"Commented" }, + { kpidEncrypted, L"Encrypted" }, + { kpidSplitBefore, L"Split Before" }, + { kpidSplitAfter, L"Split After" }, + { kpidDictionarySize, L"Dictionary Size" }, + { kpidCRC, L"CRC" }, + { kpidType, L"Type" }, + { kpidIsAnti, L"Anti" }, + { kpidMethod, L"Method" }, + { kpidHostOS, L"Host OS" }, + { kpidFileSystem, L"File System" }, + { kpidUser, L"User" }, + { kpidGroup, L"Group" }, + { kpidBlock, L"Block" }, + { kpidComment, L"Comment" }, + { kpidPosition, L"Position" }, + { kpidPrefix, L"Prefix" }, + { kpidNumSubFolders, L"Folders" }, + { kpidNumSubFiles, L"Files" }, + { kpidUnpackVer, L"Version" }, + { kpidVolume, L"Volume" }, + { kpidIsVolume, L"Multivolume" }, + { kpidOffset, L"Offset" }, + { kpidLinks, L"Links" }, + { kpidNumBlocks, L"Blocks" }, + { kpidNumVolumes, L"Volumes" } +}; + +static const char kEmptyAttributeChar = '.'; +static const char kDirectoryAttributeChar = 'D'; +static const char kReadonlyAttributeChar = 'R'; +static const char kHiddenAttributeChar = 'H'; +static const char kSystemAttributeChar = 'S'; +static const char kArchiveAttributeChar = 'A'; + +static const char *kListing = "Listing archive: "; +static const wchar_t *kFilesMessage = L"files"; +static const wchar_t *kDirsMessage = L"folders"; + +static void GetAttributesString(DWORD wa, bool directory, char *s) +{ + s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || directory) ? + kDirectoryAttributeChar: kEmptyAttributeChar; + s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0)? + kReadonlyAttributeChar: kEmptyAttributeChar; + s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? + kHiddenAttributeChar: kEmptyAttributeChar; + s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? + kSystemAttributeChar: kEmptyAttributeChar; + s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? + kArchiveAttributeChar: kEmptyAttributeChar; + s[5] = '\0'; +} + +enum EAdjustment +{ + kLeft, + kCenter, + kRight +}; + +struct CFieldInfo +{ + PROPID PropID; + UString Name; + EAdjustment TitleAdjustment; + EAdjustment TextAdjustment; + int PrefixSpacesWidth; + int Width; +}; + +struct CFieldInfoInit +{ + PROPID PropID; + const wchar_t *Name; + EAdjustment TitleAdjustment; + EAdjustment TextAdjustment; + int PrefixSpacesWidth; + int Width; +}; + +CFieldInfoInit kStandardFieldTable[] = +{ + { kpidLastWriteTime, L" Date Time", kLeft, kLeft, 0, 19 }, + { kpidAttributes, L"Attr", kRight, kCenter, 1, 5 }, + { kpidSize, L"Size", kRight, kRight, 1, 12 }, + { kpidPackedSize, L"Compressed", kRight, kRight, 1, 12 }, + { kpidPath, L"Name", kLeft, kLeft, 2, 24 } +}; + +void PrintSpaces(int numSpaces) +{ + for (int i = 0; i < numSpaces; i++) + g_StdOut << ' '; +} + +void PrintString(EAdjustment adjustment, int width, const UString &textString) +{ + const int numSpaces = width - textString.Length(); + int numLeftSpaces = 0; + switch (adjustment) + { + case kLeft: + numLeftSpaces = 0; + break; + case kCenter: + numLeftSpaces = numSpaces / 2; + break; + case kRight: + numLeftSpaces = numSpaces; + break; + } + PrintSpaces(numLeftSpaces); + g_StdOut << textString; + PrintSpaces(numSpaces - numLeftSpaces); +} + +class CFieldPrinter +{ + CObjectVector _fields; +public: + void Clear() { _fields.Clear(); } + void Init(const CFieldInfoInit *standardFieldTable, int numItems); + HRESULT Init(IInArchive *archive); + void PrintTitle(); + void PrintTitleLines(); + HRESULT PrintItemInfo(IInArchive *archive, + const UString &defaultItemName, + const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo, + UInt32 index, + bool techMode); + HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, + const UInt64 *size, const UInt64 *compressedSize); +}; + +void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems) +{ + Clear(); + for (int i = 0; i < numItems; i++) + { + CFieldInfo fieldInfo; + const CFieldInfoInit &fieldInfoInit = standardFieldTable[i]; + fieldInfo.PropID = fieldInfoInit.PropID; + fieldInfo.Name = fieldInfoInit.Name; + fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment; + fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment; + fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth; + fieldInfo.Width = fieldInfoInit.Width; + _fields.Add(fieldInfo); + } +} + +static UString GetPropName(PROPID propID, BSTR name) +{ + for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++) + { + const CPropIdToName &propIdToName = kPropIdToName[i]; + if (propIdToName.PropID == propID) + return propIdToName.Name; + } + if (name) + return name; + return L"?"; +} + +HRESULT CFieldPrinter::Init(IInArchive *archive) +{ + Clear(); + UInt32 numProps; + RINOK(archive->GetNumberOfProperties(&numProps)); + for (UInt32 i = 0; i < numProps; i++) + { + CMyComBSTR name; + PROPID propID; + VARTYPE vt; + RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt)); + CFieldInfo fieldInfo; + fieldInfo.PropID = propID; + fieldInfo.Name = GetPropName(propID, name); + _fields.Add(fieldInfo); + } + return S_OK; +} + +void CFieldPrinter::PrintTitle() +{ + for (int i = 0; i < _fields.Size(); i++) + { + const CFieldInfo &fieldInfo = _fields[i]; + PrintSpaces(fieldInfo.PrefixSpacesWidth); + PrintString(fieldInfo.TitleAdjustment, + ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name); + } +} + +void CFieldPrinter::PrintTitleLines() +{ + for (int i = 0; i < _fields.Size(); i++) + { + const CFieldInfo &fieldInfo = _fields[i]; + PrintSpaces(fieldInfo.PrefixSpacesWidth); + for (int i = 0; i < fieldInfo.Width; i++) + g_StdOut << '-'; + } +} + + +BOOL IsFileTimeZero(CONST FILETIME *lpFileTime) +{ + return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0); +} + +static const char *kEmptyTimeString = " "; +void PrintTime(const NCOM::CPropVariant &propVariant) +{ + if (propVariant.vt != VT_FILETIME) + throw "incorrect item"; + if (IsFileTimeZero(&propVariant.filetime)) + g_StdOut << kEmptyTimeString; + else + { + FILETIME localFileTime; + if (!FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime)) + throw "FileTimeToLocalFileTime error"; + char s[32]; + if (ConvertFileTimeToString(localFileTime, s, true, true)) + g_StdOut << s; + else + g_StdOut << kEmptyTimeString; + } +} + +HRESULT CFieldPrinter::PrintItemInfo(IInArchive *archive, + const UString &defaultItemName, + const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo, + UInt32 index, + bool techMode) +{ + /* + if (techMode) + { + g_StdOut << "Index = "; + g_StdOut << (UInt64)index; + g_StdOut << endl; + } + */ + for (int i = 0; i < _fields.Size(); i++) + { + const CFieldInfo &fieldInfo = _fields[i]; + if (!techMode) + PrintSpaces(fieldInfo.PrefixSpacesWidth); + + NCOM::CPropVariant propVariant; + RINOK(archive->GetProperty(index, fieldInfo.PropID, &propVariant)); + if (techMode) + { + g_StdOut << fieldInfo.Name << " = "; + } + int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width; + if (propVariant.vt == VT_EMPTY) + { + switch(fieldInfo.PropID) + { + case kpidPath: + propVariant = defaultItemName; + break; + case kpidLastWriteTime: + propVariant = archiveFileInfo.LastWriteTime; + break; + default: + if (techMode) + g_StdOut << endl; + else + PrintSpaces(width); + continue; + } + } + if (fieldInfo.PropID == kpidLastWriteTime) + { + PrintTime(propVariant); + } + else if (fieldInfo.PropID == kpidAttributes) + { + if (propVariant.vt != VT_UI4) + throw "incorrect item"; + UInt32 attributes = propVariant.ulVal; + bool isFolder; + RINOK(IsArchiveItemFolder(archive, index, isFolder)); + char s[8]; + GetAttributesString(attributes, isFolder, s); + g_StdOut << s; + } + else if (propVariant.vt == VT_BSTR) + { + if (techMode) + g_StdOut << propVariant.bstrVal; + else + PrintString(fieldInfo.TextAdjustment, width, propVariant.bstrVal); + } + else + { + UString s = ConvertPropertyToString(propVariant, fieldInfo.PropID); + s.Replace(wchar_t(0xA), L' '); + s.Replace(wchar_t(0xD), L' '); + + if (techMode) + g_StdOut << s; + else + PrintString(fieldInfo.TextAdjustment, width, s); + } + if (techMode) + g_StdOut << endl; + } + return S_OK; +} + +void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value) +{ + wchar_t textString[32] = { 0 }; + if (value != NULL) + ConvertUInt64ToString(*value, textString); + PrintString(adjustment, width, textString); +} + + +HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, + const UInt64 *size, const UInt64 *compressedSize) +{ + for (int i = 0; i < _fields.Size(); i++) + { + const CFieldInfo &fieldInfo = _fields[i]; + PrintSpaces(fieldInfo.PrefixSpacesWidth); + NCOM::CPropVariant propVariant; + if (fieldInfo.PropID == kpidSize) + PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size); + else if (fieldInfo.PropID == kpidPackedSize) + PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize); + else if (fieldInfo.PropID == kpidPath) + { + wchar_t textString[32]; + ConvertUInt64ToString(numFiles, textString); + UString temp = textString; + temp += L" "; + temp += kFilesMessage; + temp += L", "; + ConvertUInt64ToString(numDirs, textString); + temp += textString; + temp += L" "; + temp += kDirsMessage; + PrintString(fieldInfo.TextAdjustment, 0, temp); + } + else + PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L""); + } + return S_OK; +} + +bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value) +{ + NCOM::CPropVariant propVariant; + if (archive->GetProperty(index, propID, &propVariant) != S_OK) + throw "GetPropertyValue error"; + if (propVariant.vt == VT_EMPTY) + return false; + value = ConvertPropVariantToUInt64(propVariant); + return true; +} + +HRESULT ListArchives( + CCodecs *codecs, + UStringVector &archivePaths, UStringVector &archivePathsFull, + const NWildcard::CCensorNode &wildcardCensor, + bool enableHeaders, bool techMode, bool &passwordEnabled, UString &password, UInt64 &numErrors) +{ + numErrors = 0; + CFieldPrinter fieldPrinter; + if (!techMode) + fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0])); + + UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0; + UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0; + for (int i = 0; i < archivePaths.Size(); i++) + { + const UString &archiveName = archivePaths[i]; + NFile::NFind::CFileInfoW archiveFileInfo; + if (!NFile::NFind::FindFile(archiveName, archiveFileInfo) || archiveFileInfo.IsDirectory()) + { + g_StdOut << endl << "Error: " << archiveName << " is not archive" << endl; + numErrors++; + continue; + } + if (archiveFileInfo.IsDirectory()) + { + g_StdOut << endl << "Error: " << archiveName << " is not file" << endl; + numErrors++; + continue; + } + + CArchiveLink archiveLink; + + COpenCallbackConsole openCallback; + openCallback.OutStream = &g_StdOut; + openCallback.PasswordIsDefined = passwordEnabled; + openCallback.Password = password; + + HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, &openCallback); + if (result != S_OK) + { + g_StdOut << endl << "Error: " << archiveName << " is not supported archive" << endl; + numErrors++; + continue; + } + + for (int v = 0; v < archiveLink.VolumePaths.Size(); v++) + { + int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]); + if (index >= 0 && index > i) + { + archivePaths.Delete(index); + archivePathsFull.Delete(index); + } + } + + IInArchive *archive = archiveLink.GetArchive(); + const UString defaultItemName = archiveLink.GetDefaultItemName(); + + if (enableHeaders) + { + g_StdOut << endl << kListing << archiveName << endl << endl; + + UInt32 numProps; + if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK) + { + for (UInt32 i = 0; i < numProps; i++) + { + CMyComBSTR name; + PROPID propID; + VARTYPE vt; + if (archive->GetArchivePropertyInfo(i, &name, &propID, &vt) != S_OK) + continue; + NCOM::CPropVariant prop; + if (archive->GetArchiveProperty(propID, &prop) != S_OK) + continue; + UString s = ConvertPropertyToString(prop, propID); + if (!s.IsEmpty()) + g_StdOut << GetPropName(propID, name) << " = " << s << endl; + } + } + if (techMode) + g_StdOut << "----------\n"; + if (numProps > 0) + g_StdOut << endl; + } + + if (enableHeaders && !techMode) + { + fieldPrinter.PrintTitle(); + g_StdOut << endl; + fieldPrinter.PrintTitleLines(); + g_StdOut << endl; + } + + if (techMode) + { + RINOK(fieldPrinter.Init(archive)); + } + UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0; + UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0; + UInt32 numItems; + RINOK(archive->GetNumberOfItems(&numItems)); + for(UInt32 i = 0; i < numItems; i++) + { + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + + UString filePath; + RINOK(GetArchiveItemPath(archive, i, defaultItemName, filePath)); + + bool isFolder; + RINOK(IsArchiveItemFolder(archive, i, isFolder)); + if (!wildcardCensor.CheckPath(filePath, !isFolder)) + continue; + + fieldPrinter.PrintItemInfo(archive, defaultItemName, archiveFileInfo, i, techMode); + + UInt64 packSize, unpackSize; + if (!GetUInt64Value(archive, i, kpidSize, unpackSize)) + unpackSize = 0; + else + totalUnPackSizePointer = &totalUnPackSize; + if (!GetUInt64Value(archive, i, kpidPackedSize, packSize)) + packSize = 0; + else + totalPackSizePointer = &totalPackSize; + + g_StdOut << endl; + + if (isFolder) + numDirs++; + else + numFiles++; + totalPackSize += packSize; + totalUnPackSize += unpackSize; + } + if (enableHeaders && !techMode) + { + fieldPrinter.PrintTitleLines(); + g_StdOut << endl; + fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer); + g_StdOut << endl; + } + if (totalPackSizePointer != 0) + { + totalPackSizePointer2 = &totalPackSize2; + totalPackSize2 += totalPackSize; + } + if (totalUnPackSizePointer != 0) + { + totalUnPackSizePointer2 = &totalUnPackSize2; + totalUnPackSize2 += totalUnPackSize; + } + numFiles2 += numFiles; + numDirs2 += numDirs; + } + if (enableHeaders && !techMode && archivePaths.Size() > 1) + { + g_StdOut << endl; + fieldPrinter.PrintTitleLines(); + g_StdOut << endl; + fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2); + g_StdOut << endl; + g_StdOut << "Archives: " << archivePaths.Size() << endl; + } + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.h new file mode 100644 index 0000000..6e9fa24 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/List.h @@ -0,0 +1,16 @@ +// List.h + +#ifndef __LIST_H +#define __LIST_H + +#include "Common/Wildcard.h" +#include "../Common/LoadCodecs.h" + +HRESULT ListArchives( + CCodecs *codecs, + UStringVector &archivePaths, UStringVector &archivePathsFull, + const NWildcard::CCensorNode &wildcardCensor, + bool enableHeaders, bool techMode, bool &passwordEnabled, UString &password, UInt64 &errors); + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/Main.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/Main.cpp new file mode 100644 index 0000000..980f60d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/Main.cpp @@ -0,0 +1,563 @@ +// Main.cpp + +#include "StdAfx.h" + +#include "Common/MyInitGuid.h" + +#include "Common/CommandLineParser.h" +#include "Common/MyException.h" +#include "Common/IntToString.h" +#include "Common/StdOutStream.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/FileDir.h" +#include "Windows/FileName.h" +#include "Windows/Defs.h" +#include "Windows/Error.h" +#ifdef _WIN32 +#include "Windows/MemoryLock.h" +#endif + +#include "../../IPassword.h" +#include "../../ICoder.h" +#include "../Common/UpdateAction.h" +#include "../Common/Update.h" +#include "../Common/Extract.h" +#include "../Common/ArchiveCommandLine.h" +#include "../Common/ExitCode.h" +#ifdef EXTERNAL_CODECS +#include "../Common/LoadCodecs.h" +#endif + +#include "../../Compress/LZMA_Alone/LzmaBenchCon.h" + +#include "List.h" +#include "OpenCallbackConsole.h" +#include "ExtractCallbackConsole.h" +#include "UpdateCallbackConsole.h" + +#include "../../MyVersion.h" + +#if defined( _WIN32) && defined( _7ZIP_LARGE_PAGES) +extern "C" +{ +#include "../../../../C/Alloc.h" +} +#endif + +using namespace NWindows; +using namespace NFile; +using namespace NCommandLineParser; + +HINSTANCE g_hInstance = 0; +extern CStdOutStream *g_StdStream; + +static const char *kCopyrightString = "\n7-Zip" +#ifndef EXTERNAL_CODECS +" (A)" +#endif + +#ifdef _WIN64 +" [64]" +#endif + +" " MY_VERSION_COPYRIGHT_DATE "\n"; + +static const char *kHelpString = + "\nUsage: 7z" +#ifdef _NO_CRYPTO + "r" +#else +#ifndef EXTERNAL_CODECS + "a" +#endif +#endif + " [...] [...]\n" + " [<@listfiles...>]\n" + "\n" + "\n" + " a: Add files to archive\n" + " b: Benchmark\n" + " d: Delete files from archive\n" + " e: Extract files from archive (without using directory names)\n" + " l: List contents of archive\n" +// " l[a|t][f]: List contents of archive\n" +// " a - with Additional fields\n" +// " t - with all fields\n" +// " f - with Full pathnames\n" + " t: Test integrity of archive\n" + " u: Update files to archive\n" + " x: eXtract files with full paths\n" + "\n" + " -ai[r[-|0]]{@listfile|!wildcard}: Include archives\n" + " -ax[r[-|0]]{@listfile|!wildcard}: eXclude archives\n" + " -bd: Disable percentage indicator\n" + " -i[r[-|0]]{@listfile|!wildcard}: Include filenames\n" + " -m{Parameters}: set compression Method\n" + " -o{Directory}: set Output directory\n" + " -p{Password}: set Password\n" + " -r[-|0]: Recurse subdirectories\n" + " -scs{UTF-8 | WIN | DOS}: set charset for list files\n" + " -sfx[{name}]: Create SFX archive\n" + " -si[{name}]: read data from stdin\n" + " -slt: show technical information for l (List) command\n" + " -so: write data to stdout\n" + " -ssc[-]: set sensitive case mode\n" + " -ssw: compress shared files\n" + " -t{Type}: Set type of archive\n" + " -v{Size}[b|k|m|g]: Create volumes\n" + " -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: Update options\n" + " -w[{path}]: assign Work directory. Empty path means a temporary directory\n" + " -x[r[-|0]]]{@listfile|!wildcard}: eXclude filenames\n" + " -y: assume Yes on all queries\n"; + +// --------------------------- +// exception messages + +static const char *kEverythingIsOk = "Everything is Ok"; +static const char *kUserErrorMessage = "Incorrect command line"; // NExitCode::kUserError + +static const wchar_t *kDefaultSfxModule = L"7zCon.sfx"; + +static void ShowMessageAndThrowException(CStdOutStream &s, LPCSTR message, NExitCode::EEnum code) +{ + s << message << endl; + throw code; +} + +static void PrintHelpAndExit(CStdOutStream &s) // yyy +{ + s << kHelpString; + ShowMessageAndThrowException(s, kUserErrorMessage, NExitCode::kUserError); +} + +#ifndef _WIN32 +static void GetArguments(int numArguments, const char *arguments[], UStringVector &parts) +{ + parts.Clear(); + for(int i = 0; i < numArguments; i++) + { + UString s = MultiByteToUnicodeString(arguments[i]); + parts.Add(s); + } +} +#endif + +static void ShowCopyrightAndHelp(CStdOutStream &s, bool needHelp) +{ + s << kCopyrightString; + // s << "# CPUs: " << (UInt64)NWindows::NSystem::GetNumberOfProcessors() << "\n"; + if (needHelp) + s << kHelpString; +} + +#ifdef EXTERNAL_CODECS +static void PrintString(CStdOutStream &stdStream, const AString &s, int size) +{ + int len = s.Length(); + stdStream << s; + for (int i = len; i < size; i++) + stdStream << ' '; +} +#endif + +static void PrintString(CStdOutStream &stdStream, const UString &s, int size) +{ + int len = s.Length(); + stdStream << s; + for (int i = len; i < size; i++) + stdStream << ' '; +} + +static inline char GetHex(Byte value) +{ + return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); +} + +int Main2( + #ifndef _WIN32 + int numArguments, const char *arguments[] + #endif +) +{ + #ifdef _WIN32 + SetFileApisToOEM(); + #endif + + UStringVector commandStrings; + #ifdef _WIN32 + NCommandLineParser::SplitCommandLine(GetCommandLineW(), commandStrings); + #else + GetArguments(numArguments, arguments, commandStrings); + #endif + + if(commandStrings.Size() == 1) + { + ShowCopyrightAndHelp(g_StdOut, true); + return 0; + } + commandStrings.Delete(0); + + CArchiveCommandLineOptions options; + + CArchiveCommandLineParser parser; + + parser.Parse1(commandStrings, options); + + if(options.HelpMode) + { + ShowCopyrightAndHelp(g_StdOut, true); + return 0; + } + + #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES) + if (options.LargePages) + { + SetLargePageSize(); + NSecurity::EnableLockMemoryPrivilege(); + } + #endif + + CStdOutStream &stdStream = options.StdOutMode ? g_StdErr : g_StdOut; + g_StdStream = &stdStream; + + if (options.EnableHeaders) + ShowCopyrightAndHelp(stdStream, false); + + parser.Parse2(options); + + CCodecs *codecs = new CCodecs; + CMyComPtr< + #ifdef EXTERNAL_CODECS + ICompressCodecsInfo + #else + IUnknown + #endif + > compressCodecsInfo = codecs; + HRESULT result = codecs->Load(); + if (result != S_OK) + throw CSystemException(result); + + bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); + if (options.Command.CommandType == NCommandType::kInfo) + { + stdStream << endl << "Formats:" << endl; + int i; + for (i = 0; i < codecs->Formats.Size(); i++) + { + const CArcInfoEx &arc = codecs->Formats[i]; + #ifdef EXTERNAL_CODECS + if (arc.LibIndex >= 0) + { + char s[32]; + ConvertUInt64ToString(arc.LibIndex, s); + PrintString(stdStream, s, 2); + } + else + #endif + stdStream << " "; + stdStream << ' '; + stdStream << (char)(arc.UpdateEnabled ? 'C' : ' '); + stdStream << (char)(arc.KeepName ? 'K' : ' '); + stdStream << " "; + PrintString(stdStream, arc.Name, 6); + stdStream << " "; + UString s; + for (int t = 0; t < arc.Exts.Size(); t++) + { + const CArcExtInfo &ext = arc.Exts[t]; + s += ext.Ext; + if (!ext.AddExt.IsEmpty()) + { + s += L" ("; + s += ext.AddExt; + s += L')'; + } + s += L' '; + } + PrintString(stdStream, s, 14); + stdStream << " "; + const CByteBuffer &sig = arc.StartSignature; + for (size_t j = 0; j < sig.GetCapacity(); j++) + { + Byte b = sig[j]; + if (b > 0x20 && b < 0x80) + { + stdStream << (char)b; + } + else + { + stdStream << GetHex((Byte)((b >> 4) & 0xF)); + stdStream << GetHex((Byte)(b & 0xF)); + } + stdStream << ' '; + } + stdStream << endl; + } + stdStream << endl << "Codecs:" << endl; + + #ifdef EXTERNAL_CODECS + UINT32 numMethods; + if (codecs->GetNumberOfMethods(&numMethods) == S_OK) + for (UInt32 j = 0; j < numMethods; j++) + { + int libIndex = codecs->GetCodecLibIndex(j); + if (libIndex >= 0) + { + char s[32]; + ConvertUInt64ToString(libIndex, s); + PrintString(stdStream, s, 2); + } + else + stdStream << " "; + stdStream << ' '; + stdStream << (char)(codecs->GetCodecEncoderIsAssigned(j) ? 'C' : ' '); + UInt64 id; + stdStream << " "; + HRESULT res = codecs->GetCodecId(j, id); + if (res != S_OK) + id = (UInt64)(Int64)-1; + char s[32]; + ConvertUInt64ToString(id, s, 16); + PrintString(stdStream, s, 8); + stdStream << " "; + PrintString(stdStream, codecs->GetCodecName(j), 11); + stdStream << endl; + /* + if (res != S_OK) + throw "incorrect Codec ID"; + */ + } + #endif + return S_OK; + } + else if (options.Command.CommandType == NCommandType::kBenchmark) + { + if (options.Method.CompareNoCase(L"CRC") == 0) + { + HRESULT res = CrcBenchCon((FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize); + if (res != S_OK) + { + if (res == S_FALSE) + { + stdStream << "\nCRC Error\n"; + return NExitCode::kFatalError; + } + throw CSystemException(res); + } + } + else + { + HRESULT res = LzmaBenchCon( + #ifdef EXTERNAL_LZMA + codecs, + #endif + (FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize); + if (res != S_OK) + { + if (res == S_FALSE) + { + stdStream << "\nDecoding Error\n"; + return NExitCode::kFatalError; + } + throw CSystemException(res); + } + } + } + else if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList) + { + if(isExtractGroupCommand) + { + CExtractCallbackConsole *ecs = new CExtractCallbackConsole; + CMyComPtr extractCallback = ecs; + + ecs->OutStream = &stdStream; + ecs->PasswordIsDefined = options.PasswordEnabled; + ecs->Password = options.Password; + ecs->Init(); + + COpenCallbackConsole openCallback; + openCallback.OutStream = &stdStream; + openCallback.PasswordIsDefined = options.PasswordEnabled; + openCallback.Password = options.Password; + + CExtractOptions eo; + eo.StdOutMode = options.StdOutMode; + eo.PathMode = options.Command.GetPathMode(); + eo.TestMode = options.Command.IsTestMode(); + eo.OverwriteMode = options.OverwriteMode; + eo.OutputDir = options.OutputDir; + eo.YesToAll = options.YesToAll; + #ifdef COMPRESS_MT + eo.Properties = options.ExtractProperties; + #endif + UString errorMessage; + CDecompressStat stat; + HRESULT result = DecompressArchives( + codecs, + options.ArchivePathsSorted, + options.ArchivePathsFullSorted, + options.WildcardCensor.Pairs.Front().Head, + eo, &openCallback, ecs, errorMessage, stat); + if (!errorMessage.IsEmpty()) + { + stdStream << endl << "Error: " << errorMessage; + if (result == S_OK) + result = E_FAIL; + } + + stdStream << endl; + if (ecs->NumArchives > 1) + stdStream << "Archives: " << ecs->NumArchives << endl; + if (ecs->NumArchiveErrors != 0 || ecs->NumFileErrors != 0) + { + if (ecs->NumArchives > 1) + { + stdStream << endl; + if (ecs->NumArchiveErrors != 0) + stdStream << "Archive Errors: " << ecs->NumArchiveErrors << endl; + if (ecs->NumFileErrors != 0) + stdStream << "Sub items Errors: " << ecs->NumFileErrors << endl; + } + if (result != S_OK) + throw CSystemException(result); + return NExitCode::kFatalError; + } + if (result != S_OK) + throw CSystemException(result); + if (stat.NumFolders != 0) + stdStream << "Folders: " << stat.NumFolders << endl; + if (stat.NumFiles != 1 || stat.NumFolders != 0) + stdStream << "Files: " << stat.NumFiles << endl; + stdStream + << "Size: " << stat.UnpackSize << endl + << "Compressed: " << stat.PackSize << endl; + } + else + { + UInt64 numErrors = 0; + HRESULT result = ListArchives( + codecs, + options.ArchivePathsSorted, + options.ArchivePathsFullSorted, + options.WildcardCensor.Pairs.Front().Head, + options.EnableHeaders, + options.TechMode, + options.PasswordEnabled, + options.Password, numErrors); + if (numErrors > 0) + { + g_StdOut << endl << "Errors: " << numErrors; + return NExitCode::kFatalError; + } + if (result != S_OK) + throw CSystemException(result); + } + } + else if(options.Command.IsFromUpdateGroup()) + { + UString workingDir; + + CUpdateOptions &uo = options.UpdateOptions; + if (uo.SfxMode && uo.SfxModule.IsEmpty()) + uo.SfxModule = kDefaultSfxModule; + + bool passwordIsDefined = + options.PasswordEnabled && !options.Password.IsEmpty(); + + COpenCallbackConsole openCallback; + openCallback.OutStream = &stdStream; + openCallback.PasswordIsDefined = passwordIsDefined; + openCallback.Password = options.Password; + + CUpdateCallbackConsole callback; + callback.EnablePercents = options.EnablePercents; + callback.PasswordIsDefined = passwordIsDefined; + callback.AskPassword = options.PasswordEnabled && options.Password.IsEmpty(); + callback.Password = options.Password; + callback.StdOutMode = uo.StdOutMode; + callback.Init(&stdStream); + + CUpdateErrorInfo errorInfo; + + if (!uo.Init(codecs, options.ArchiveName, options.ArcType)) + throw "Unsupported archive type"; + HRESULT result = UpdateArchive(codecs, + options.WildcardCensor, uo, + errorInfo, &openCallback, &callback); + + int exitCode = NExitCode::kSuccess; + if (callback.CantFindFiles.Size() > 0) + { + stdStream << endl; + stdStream << "WARNINGS for files:" << endl << endl; + int numErrors = callback.CantFindFiles.Size(); + for (int i = 0; i < numErrors; i++) + { + stdStream << callback.CantFindFiles[i] << " : "; + stdStream << NError::MyFormatMessageW(callback.CantFindCodes[i]) << endl; + } + stdStream << "----------------" << endl; + stdStream << "WARNING: Cannot find " << numErrors << " file"; + if (numErrors > 1) + stdStream << "s"; + stdStream << endl; + exitCode = NExitCode::kWarning; + } + + if (result != S_OK) + { + UString message; + if (!errorInfo.Message.IsEmpty()) + { + message += errorInfo.Message; + message += L"\n"; + } + if (!errorInfo.FileName.IsEmpty()) + { + message += errorInfo.FileName; + message += L"\n"; + } + if (!errorInfo.FileName2.IsEmpty()) + { + message += errorInfo.FileName2; + message += L"\n"; + } + if (errorInfo.SystemError != 0) + { + message += NError::MyFormatMessageW(errorInfo.SystemError); + message += L"\n"; + } + if (!message.IsEmpty()) + stdStream << L"\nError:\n" << message; + throw CSystemException(result); + } + int numErrors = callback.FailedFiles.Size(); + if (numErrors == 0) + { + if (callback.CantFindFiles.Size() == 0) + stdStream << kEverythingIsOk << endl; + } + else + { + stdStream << endl; + stdStream << "WARNINGS for files:" << endl << endl; + for (int i = 0; i < numErrors; i++) + { + stdStream << callback.FailedFiles[i] << " : "; + stdStream << NError::MyFormatMessageW(callback.FailedCodes[i]) << endl; + } + stdStream << "----------------" << endl; + stdStream << "WARNING: Cannot open " << numErrors << " file"; + if (numErrors > 1) + stdStream << "s"; + stdStream << endl; + exitCode = NExitCode::kWarning; + } + return exitCode; + } + else + PrintHelpAndExit(stdStream); + return 0; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/MainAr.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/MainAr.cpp new file mode 100644 index 0000000..fd42e4f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/MainAr.cpp @@ -0,0 +1,161 @@ +// MainAr.cpp + +#include "StdAfx.h" + +// #include + +#include "Windows/Error.h" + +#include "Common/StdOutStream.h" +#include "Common/NewHandler.h" +#include "Common/MyException.h" +#include "Common/StringConvert.h" + +#include "../Common/ExitCode.h" +#include "../Common/ArchiveCommandLine.h" +#include "ConsoleClose.h" + +using namespace NWindows; + +CStdOutStream *g_StdStream = 0; + +#ifdef _WIN32 +#ifndef _UNICODE +bool g_IsNT = false; +#endif +#if !defined(_UNICODE) || !defined(_WIN64) +static inline bool IsItWindowsNT() +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + if (!::GetVersionEx(&versionInfo)) + return false; + return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); +} +#endif +#endif + +extern int Main2( + #ifndef _WIN32 + int numArguments, const char *arguments[] + #endif +); + +static const char *kExceptionErrorMessage = "\n\nError:\n"; +static const char *kUserBreak = "\nBreak signaled\n"; + +static const char *kMemoryExceptionMessage = "\n\nERROR: Can't allocate required memory!\n"; +static const char *kUnknownExceptionMessage = "\n\nUnknown Error\n"; +static const char *kInternalExceptionMessage = "\n\nInternal Error #"; + +int +#ifdef _MSC_VER +__cdecl +#endif +main +( +#ifndef _WIN32 +int numArguments, const char *arguments[] +#endif +) +{ + g_StdStream = &g_StdOut; + #ifdef _WIN32 + + #ifdef _UNICODE + #ifndef _WIN64 + if (!IsItWindowsNT()) + { + (*g_StdStream) << "This program requires Windows NT/2000/XP/2003/Vista"; + return NExitCode::kFatalError; + } + #endif + #else + g_IsNT = IsItWindowsNT(); + #endif + + #endif + + // setlocale(LC_COLLATE, ".OCP"); + NConsoleClose::CCtrlHandlerSetter ctrlHandlerSetter; + int res = 0; + try + { + res = Main2( +#ifndef _WIN32 + numArguments, arguments +#endif + ); + } + catch(const CNewException &) + { + (*g_StdStream) << kMemoryExceptionMessage; + return (NExitCode::kMemoryError); + } + catch(const NConsoleClose::CCtrlBreakException &) + { + (*g_StdStream) << endl << kUserBreak; + return (NExitCode::kUserBreak); + } + catch(const CArchiveCommandLineException &e) + { + (*g_StdStream) << kExceptionErrorMessage << e << endl; + return (NExitCode::kUserError); + } + catch(const CSystemException &systemError) + { + if (systemError.ErrorCode == E_OUTOFMEMORY) + { + (*g_StdStream) << kMemoryExceptionMessage; + return (NExitCode::kMemoryError); + } + if (systemError.ErrorCode == E_ABORT) + { + (*g_StdStream) << endl << kUserBreak; + return (NExitCode::kUserBreak); + } + UString message; + NError::MyFormatMessage(systemError.ErrorCode, message); + (*g_StdStream) << endl << endl << "System error:" << endl << + message << endl; + return (NExitCode::kFatalError); + } + catch(NExitCode::EEnum &exitCode) + { + (*g_StdStream) << kInternalExceptionMessage << exitCode << endl; + return (exitCode); + } + /* + catch(const NExitCode::CMultipleErrors &multipleErrors) + { + (*g_StdStream) << endl << multipleErrors.NumErrors << " errors" << endl; + return (NExitCode::kFatalError); + } + */ + catch(const UString &s) + { + (*g_StdStream) << kExceptionErrorMessage << s << endl; + return (NExitCode::kFatalError); + } + catch(const AString &s) + { + (*g_StdStream) << kExceptionErrorMessage << s << endl; + return (NExitCode::kFatalError); + } + catch(const char *s) + { + (*g_StdStream) << kExceptionErrorMessage << s << endl; + return (NExitCode::kFatalError); + } + catch(int t) + { + (*g_StdStream) << kInternalExceptionMessage << t << endl; + return (NExitCode::kFatalError); + } + catch(...) + { + (*g_StdStream) << kUnknownExceptionMessage; + return (NExitCode::kFatalError); + } + return res; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp new file mode 100644 index 0000000..06ff165 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp @@ -0,0 +1,58 @@ +// OpenCallbackConsole.cpp + +#include "StdAfx.h" + +#include "OpenCallbackConsole.h" + +#include "ConsoleClose.h" +#include "UserInputUtils.h" + +HRESULT COpenCallbackConsole::CheckBreak() +{ + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + return S_OK; +} + +HRESULT COpenCallbackConsole::SetTotal(const UInt64 *, const UInt64 *) +{ + return CheckBreak(); +} + +HRESULT COpenCallbackConsole::SetCompleted(const UInt64 *, const UInt64 *) +{ + return CheckBreak(); +} + +HRESULT COpenCallbackConsole::CryptoGetTextPassword(BSTR *password) +{ + PasswordWasAsked = true; + RINOK(CheckBreak()); + if (!PasswordIsDefined) + { + Password = GetPassword(OutStream); + PasswordIsDefined = true; + } + CMyComBSTR temp(Password); + *password = temp.Detach(); + return S_OK; +} + +HRESULT COpenCallbackConsole::GetPasswordIfAny(UString &password) +{ + if (PasswordIsDefined) + password = Password; + return S_OK; +} + +bool COpenCallbackConsole::WasPasswordAsked() +{ + return PasswordWasAsked; +} + +void COpenCallbackConsole::ClearPasswordWasAskedFlag() +{ + PasswordWasAsked = false; +} + + diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h new file mode 100644 index 0000000..db0e9bd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h @@ -0,0 +1,27 @@ +// OpenCallbackConsole.h + +#ifndef __OPENCALLBACKCONSOLE_H +#define __OPENCALLBACKCONSOLE_H + +#include "Common/StdOutStream.h" +#include "../Common/ArchiveOpenCallback.h" + +class COpenCallbackConsole: public IOpenCallbackUI +{ +public: + HRESULT CheckBreak(); + HRESULT SetTotal(const UInt64 *files, const UInt64 *bytes); + HRESULT SetCompleted(const UInt64 *files, const UInt64 *bytes); + HRESULT CryptoGetTextPassword(BSTR *password); + HRESULT GetPasswordIfAny(UString &password); + bool WasPasswordAsked(); + void ClearPasswordWasAskedFlag(); + + CStdOutStream *OutStream; + bool PasswordIsDefined; + UString Password; + bool PasswordWasAsked; + COpenCallbackConsole(): PasswordIsDefined(false), PasswordWasAsked(false) {} +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp new file mode 100644 index 0000000..47aafd7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp @@ -0,0 +1,90 @@ +// PercentPrinter.cpp + +#include "StdAfx.h" + +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "PercentPrinter.h" + +const int kPaddingSize = 2; +const int kPercentsSize = 4; +const int kMaxExtraSize = kPaddingSize + 32 + kPercentsSize; + +static void ClearPrev(char *p, int num) +{ + int i; + for (i = 0; i < num; i++) *p++ = '\b'; + for (i = 0; i < num; i++) *p++ = ' '; + for (i = 0; i < num; i++) *p++ = '\b'; + *p = '\0'; +} + +void CPercentPrinter::ClosePrint() +{ + if (m_NumExtraChars == 0) + return; + char s[kMaxExtraSize * 3 + 1]; + ClearPrev(s, m_NumExtraChars); + (*OutStream) << s; + m_NumExtraChars = 0; +} + +void CPercentPrinter::PrintString(const char *s) +{ + ClosePrint(); + (*OutStream) << s; +} + +void CPercentPrinter::PrintString(const wchar_t *s) +{ + ClosePrint(); + (*OutStream) << s; +} + +void CPercentPrinter::PrintNewLine() +{ + ClosePrint(); + (*OutStream) << "\n"; +} + +void CPercentPrinter::RePrintRatio() +{ + char s[32]; + ConvertUInt64ToString(((m_Total == 0) ? 0 : (m_CurValue * 100 / m_Total)), s); + int size = (int)strlen(s); + s[size++] = '%'; + s[size] = '\0'; + + int extraSize = kPaddingSize + MyMax(size, kPercentsSize); + if (extraSize < m_NumExtraChars) + extraSize = m_NumExtraChars; + + char fullString[kMaxExtraSize * 3]; + char *p = fullString; + int i; + if (m_NumExtraChars == 0) + { + for (i = 0; i < extraSize; i++) + *p++ = ' '; + m_NumExtraChars = extraSize; + } + + for (i = 0; i < m_NumExtraChars; i++) + *p++ = '\b'; + m_NumExtraChars = extraSize; + for (; size < m_NumExtraChars; size++) + *p++ = ' '; + MyStringCopy(p, s); + (*OutStream) << fullString; + OutStream->Flush(); + m_PrevValue = m_CurValue; +} + +void CPercentPrinter::PrintRatio() +{ + if (m_CurValue < m_PrevValue + m_MinStepSize && + m_CurValue + m_MinStepSize > m_PrevValue && m_NumExtraChars != 0) + return; + RePrintRatio(); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.h new file mode 100644 index 0000000..e8b4091 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/PercentPrinter.h @@ -0,0 +1,31 @@ +// PercentPrinter.h + +#ifndef __PERCENTPRINTER_H +#define __PERCENTPRINTER_H + +#include "Common/Types.h" +#include "Common/StdOutStream.h" + +class CPercentPrinter +{ + UInt64 m_MinStepSize; + UInt64 m_PrevValue; + UInt64 m_CurValue; + UInt64 m_Total; + int m_NumExtraChars; +public: + CStdOutStream *OutStream; + + CPercentPrinter(UInt64 minStepSize = 1): m_MinStepSize(minStepSize), + m_PrevValue(0), m_CurValue(0), m_Total(1), m_NumExtraChars(0) {} + void SetTotal(UInt64 total) { m_Total = total; m_PrevValue = 0; } + void SetRatio(UInt64 doneValue) { m_CurValue = doneValue; } + void PrintString(const char *s); + void PrintString(const wchar_t *s); + void PrintNewLine(); + void ClosePrint(); + void RePrintRatio(); + void PrintRatio(); +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.cpp new file mode 100644 index 0000000..d0feea8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.cpp @@ -0,0 +1,3 @@ +// StdAfx.cpp + +#include "StdAfx.h" diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.h new file mode 100644 index 0000000..8531cc9 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" +#include "../../../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp new file mode 100644 index 0000000..1d4420d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp @@ -0,0 +1,208 @@ +// UpdateCallbackConsole.cpp + +#include "StdAfx.h" + +#include "UpdateCallbackConsole.h" + +#include "Windows/Error.h" +#ifdef COMPRESS_MT +#include "Windows/Synchronization.h" +#endif + +#include "ConsoleClose.h" +#include "UserInputUtils.h" + +using namespace NWindows; + +#ifdef COMPRESS_MT +static NSynchronization::CCriticalSection g_CriticalSection; +#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection); +#else +#define MT_LOCK +#endif + +static const wchar_t *kEmptyFileAlias = L"[Content]"; + +static const char *kCreatingArchiveMessage = "Creating archive "; +static const char *kUpdatingArchiveMessage = "Updating archive "; +static const char *kScanningMessage = "Scanning"; + + +HRESULT CUpdateCallbackConsole::OpenResult(const wchar_t *name, HRESULT result) +{ + (*OutStream) << endl; + if (result != S_OK) + (*OutStream) << "Error: " << name << " is not supported archive" << endl; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::StartScanning() +{ + (*OutStream) << kScanningMessage; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::CanNotFindError(const wchar_t *name, DWORD systemError) +{ + CantFindFiles.Add(name); + CantFindCodes.Add(systemError); + // m_PercentPrinter.ClosePrint(); + if (!m_WarningsMode) + { + (*OutStream) << endl << endl; + m_PercentPrinter.PrintNewLine(); + m_WarningsMode = true; + } + m_PercentPrinter.PrintString(name); + m_PercentPrinter.PrintString(": WARNING: "); + m_PercentPrinter.PrintString(NError::MyFormatMessageW(systemError)); + m_PercentPrinter.PrintNewLine(); + return S_OK; +} + +HRESULT CUpdateCallbackConsole::FinishScanning() +{ + (*OutStream) << endl << endl; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::StartArchive(const wchar_t *name, bool updating) +{ + if(updating) + (*OutStream) << kUpdatingArchiveMessage; + else + (*OutStream) << kCreatingArchiveMessage; + if (name != 0) + (*OutStream) << name; + else + (*OutStream) << "StdOut"; + (*OutStream) << endl << endl; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::FinishArchive() +{ + (*OutStream) << endl; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::CheckBreak() +{ + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::Finilize() +{ + MT_LOCK + if (m_NeedBeClosed) + { + if (EnablePercents) + { + m_PercentPrinter.ClosePrint(); + } + if (!StdOutMode && m_NeedNewLine) + { + m_PercentPrinter.PrintNewLine(); + m_NeedNewLine = false; + } + m_NeedBeClosed = false; + } + return S_OK; +} + +HRESULT CUpdateCallbackConsole::SetNumFiles(UInt64 /* numFiles */) +{ + return S_OK; +} + +HRESULT CUpdateCallbackConsole::SetTotal(UInt64 size) +{ + MT_LOCK + if (EnablePercents) + m_PercentPrinter.SetTotal(size); + return S_OK; +} + +HRESULT CUpdateCallbackConsole::SetCompleted(const UInt64 *completeValue) +{ + MT_LOCK + if (completeValue != NULL) + { + if (EnablePercents) + { + m_PercentPrinter.SetRatio(*completeValue); + m_PercentPrinter.PrintRatio(); + m_NeedBeClosed = true; + } + } + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 * /* outSize */) +{ + /* + if (NConsoleClose::TestBreakSignal()) + return E_ABORT; + */ + return S_OK; +} + +HRESULT CUpdateCallbackConsole::GetStream(const wchar_t *name, bool isAnti) +{ + MT_LOCK + if (StdOutMode) + return S_OK; + if(isAnti) + m_PercentPrinter.PrintString("Anti item "); + else + m_PercentPrinter.PrintString("Compressing "); + if (name[0] == 0) + name = kEmptyFileAlias; + m_PercentPrinter.PrintString(name); + if (EnablePercents) + m_PercentPrinter.RePrintRatio(); + return S_OK; +} + +HRESULT CUpdateCallbackConsole::OpenFileError(const wchar_t *name, DWORD systemError) +{ + MT_LOCK + FailedCodes.Add(systemError); + FailedFiles.Add(name); + // if (systemError == ERROR_SHARING_VIOLATION) + { + m_PercentPrinter.ClosePrint(); + m_PercentPrinter.PrintNewLine(); + m_PercentPrinter.PrintString("WARNING: "); + m_PercentPrinter.PrintString(NError::MyFormatMessageW(systemError)); + return S_FALSE; + } + // return systemError; +} + +HRESULT CUpdateCallbackConsole::SetOperationResult(Int32 ) +{ + m_NeedBeClosed = true; + m_NeedNewLine = true; + return S_OK; +} + +HRESULT CUpdateCallbackConsole::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) +{ + if (!PasswordIsDefined) + { + if (AskPassword) + { + Password = GetPassword(OutStream); + PasswordIsDefined = true; + } + } + *passwordIsDefined = BoolToInt(PasswordIsDefined); + CMyComBSTR tempName(Password); + *password = tempName.Detach(); + return S_OK; +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h new file mode 100644 index 0000000..d04e1ad --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h @@ -0,0 +1,58 @@ +// UpdateCallbackConsole.h + +#ifndef __UPDATECALLBACKCONSOLE_H +#define __UPDATECALLBACKCONSOLE_H + +#include "Common/MyString.h" +#include "Common/StdOutStream.h" +#include "PercentPrinter.h" +#include "../Common/Update.h" + +class CUpdateCallbackConsole: public IUpdateCallbackUI2 +{ + CPercentPrinter m_PercentPrinter; + bool m_NeedBeClosed; + bool m_NeedNewLine; + + bool m_WarningsMode; + + CStdOutStream *OutStream; +public: + bool EnablePercents; + bool StdOutMode; + + bool PasswordIsDefined; + UString Password; + bool AskPassword; + + + CUpdateCallbackConsole(): + m_PercentPrinter(1 << 16), + PasswordIsDefined(false), + AskPassword(false), + StdOutMode(false), + EnablePercents(true), + m_WarningsMode(false) + {} + + ~CUpdateCallbackConsole() { Finilize(); } + void Init(CStdOutStream *outStream) + { + m_NeedBeClosed = false; + m_NeedNewLine = false; + FailedFiles.Clear(); + FailedCodes.Clear(); + OutStream = outStream; + m_PercentPrinter.OutStream = outStream; + } + + INTERFACE_IUpdateCallbackUI2(;) + + UStringVector FailedFiles; + CRecordVector FailedCodes; + + UStringVector CantFindFiles; + CRecordVector CantFindCodes; +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp new file mode 100644 index 0000000..164af99 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp @@ -0,0 +1,58 @@ +// UserInputUtils.cpp + +#include "StdAfx.h" + +#include "Common/StdInStream.h" +#include "Common/StringConvert.h" + +#include "UserInputUtils.h" + +static const char kYes = 'Y'; +static const char kNo = 'N'; +static const char kYesAll = 'A'; +static const char kNoAll = 'S'; +static const char kAutoRename = 'U'; +static const char kQuit = 'Q'; + +static const char *kFirstQuestionMessage = "?\n"; +static const char *kHelpQuestionMessage = + "(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename / (Q)uit? "; + +// return true if pressed Quite; +// in: anAll +// out: anAll, anYes; + +NUserAnswerMode::EEnum ScanUserYesNoAllQuit(CStdOutStream *outStream) +{ + (*outStream) << kFirstQuestionMessage; + for(;;) + { + (*outStream) << kHelpQuestionMessage; + AString scannedString = g_StdIn.ScanStringUntilNewLine(); + scannedString.Trim(); + if(!scannedString.IsEmpty()) + switch(::MyCharUpper(scannedString[0])) + { + case kYes: + return NUserAnswerMode::kYes; + case kNo: + return NUserAnswerMode::kNo; + case kYesAll: + return NUserAnswerMode::kYesAll; + case kNoAll: + return NUserAnswerMode::kNoAll; + case kAutoRename: + return NUserAnswerMode::kAutoRename; + case kQuit: + return NUserAnswerMode::kQuit; + } + } +} + +UString GetPassword(CStdOutStream *outStream) +{ + (*outStream) << "\nEnter password:"; + outStream->Flush(); + AString oemPassword = g_StdIn.ScanStringUntilNewLine(); + return MultiByteToUnicodeString(oemPassword, CP_OEMCP); +} diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.h new file mode 100644 index 0000000..408e93e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/UserInputUtils.h @@ -0,0 +1,24 @@ +// UserInputUtils.h + +#ifndef __USERINPUTUTILS_H +#define __USERINPUTUTILS_H + +#include "Common/StdOutStream.h" + +namespace NUserAnswerMode { + +enum EEnum +{ + kYes, + kNo, + kYesAll, + kNoAll, + kAutoRename, + kQuit +}; +} + +NUserAnswerMode::EEnum ScanUserYesNoAllQuit(CStdOutStream *outStream); +UString GetPassword(CStdOutStream *outStream); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/7zip/UI/Console/afxres.h b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/afxres.h new file mode 100644 index 0000000..c2fadd4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/7zip/UI/Console/afxres.h @@ -0,0 +1 @@ +#include diff --git a/3rdparty/physfs/lzma/CPP/Build.mak b/3rdparty/physfs/lzma/CPP/Build.mak new file mode 100644 index 0000000..d259556 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Build.mak @@ -0,0 +1,68 @@ +!IFDEF CPU +LIBS = $(LIBS) bufferoverflowU.lib +CFLAGS = $(CFLAGS) -GS- -Zc:forScope +!ENDIF + +!IFNDEF O +!IFDEF CPU +O=$(CPU) +!ELSE +O=O +!ENDIF +!ENDIF + +!IF "$(CPU)" != "IA64" +!IF "$(CPU)" != "AMD64" +MY_ML = ml +!ELSE +MY_ML = ml64 +!ENDIF +!ENDIF + +COMPL_ASM = $(MY_ML) -c -Fo$O/ $** + +CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ -EHsc -Gz -WX -Gy + +!IFDEF MY_STATIC_LINK +!IFNDEF MY_SINGLE_THREAD +CFLAGS = $(CFLAGS) -MT +!ENDIF +!ELSE +CFLAGS = $(CFLAGS) -MD +!ENDIF + +!IFDEF NEW_COMPILER +CFLAGS_O1 = $(CFLAGS) -O1 -W4 -Wp64 +CFLAGS_O2 = $(CFLAGS) -O2 -W4 -Wp64 +!ELSE +CFLAGS_O1 = $(CFLAGS) -O1 -W3 +CFLAGS_O2 = $(CFLAGS) -O2 -W3 +!ENDIF + +LFLAGS = $(LFLAGS) -nologo -OPT:NOWIN98 -OPT:REF + +!IFDEF DEF_FILE +LFLAGS = $(LFLAGS) -DLL -DEF:$(DEF_FILE) +!ENDIF + +PROGPATH = $O\$(PROG) + +COMPL_O1 = $(CPP) $(CFLAGS_O1) $** +COMPL_O2 = $(CPP) $(CFLAGS_O2) $** +COMPL_PCH = $(CPP) $(CFLAGS_O1) -Yc"StdAfx.h" -Fp$O/a.pch $** +COMPL = $(CPP) $(CFLAGS_O1) -Yu"StdAfx.h" -Fp$O/a.pch $** + +all: $(PROGPATH) + +clean: + -del /Q $(PROGPATH) $O\*.exe $O\*.dll $O\*.obj $O\*.lib $O\*.exp $O\*.res $O\*.pch + +$O: + if not exist "$O" mkdir "$O" + +$(PROGPATH): $O $(OBJS) $(DEF_FILE) + link $(LFLAGS) -out:$(PROGPATH) $(OBJS) $(LIBS) +$O\resource.res: $(*B).rc + rc -fo$@ $** +$O\StdAfx.obj: $(*B).cpp + $(COMPL_PCH) diff --git a/3rdparty/physfs/lzma/CPP/Common/AutoPtr.h b/3rdparty/physfs/lzma/CPP/Common/AutoPtr.h new file mode 100644 index 0000000..c5808cb --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/AutoPtr.h @@ -0,0 +1,35 @@ +// Common/AutoPtr.h + +#ifndef __COMMON_AUTOPTR_H +#define __COMMON_AUTOPTR_H + +template class CMyAutoPtr +{ + T *_p; +public: + CMyAutoPtr(T *p = 0) : _p(p) {} + CMyAutoPtr(CMyAutoPtr& p): _p(p.release()) {} + CMyAutoPtr& operator=(CMyAutoPtr& p) + { + reset(p.release()); + return (*this); + } + ~CMyAutoPtr() { delete _p; } + T& operator*() const { return *_p; } + // T* operator->() const { return (&**this); } + T* get() const { return _p; } + T* release() + { + T *tmp = _p; + _p = 0; + return tmp; + } + void reset(T* p = 0) + { + if (p != _p) + delete _p; + _p = p; + } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/Buffer.h b/3rdparty/physfs/lzma/CPP/Common/Buffer.h new file mode 100644 index 0000000..78a68a6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/Buffer.h @@ -0,0 +1,77 @@ +// Common/Buffer.h + +#ifndef __COMMON_BUFFER_H +#define __COMMON_BUFFER_H + +#include "Defs.h" + +template class CBuffer +{ +protected: + size_t _capacity; + T *_items; +public: + void Free() + { + delete []_items; + _items = 0; + _capacity = 0; + } + CBuffer(): _capacity(0), _items(0) {}; + CBuffer(const CBuffer &buffer): _capacity(0), _items(0) { *this = buffer; } + CBuffer(size_t size): _items(0), _capacity(0) { SetCapacity(size); } + virtual ~CBuffer() { delete []_items; } + operator T *() { return _items; }; + operator const T *() const { return _items; }; + size_t GetCapacity() const { return _capacity; } + void SetCapacity(size_t newCapacity) + { + if (newCapacity == _capacity) + return; + T *newBuffer; + if (newCapacity > 0) + { + newBuffer = new T[newCapacity]; + if(_capacity > 0) + memmove(newBuffer, _items, MyMin(_capacity, newCapacity) * sizeof(T)); + } + else + newBuffer = 0; + delete []_items; + _items = newBuffer; + _capacity = newCapacity; + } + CBuffer& operator=(const CBuffer &buffer) + { + Free(); + if(buffer._capacity > 0) + { + SetCapacity(buffer._capacity); + memmove(_items, buffer._items, buffer._capacity * sizeof(T)); + } + return *this; + } +}; + +template +bool operator==(const CBuffer& b1, const CBuffer& b2) +{ + if (b1.GetCapacity() != b2.GetCapacity()) + return false; + for (size_t i = 0; i < b1.GetCapacity(); i++) + if (b1[i] != b2[i]) + return false; + return true; +} + +template +bool operator!=(const CBuffer& b1, const CBuffer& b2) +{ + return !(b1 == b2); +} + +typedef CBuffer CCharBuffer; +typedef CBuffer CWCharBuffer; +typedef CBuffer CByteBuffer; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/CRC.cpp b/3rdparty/physfs/lzma/CPP/Common/CRC.cpp new file mode 100644 index 0000000..b768128 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/CRC.cpp @@ -0,0 +1,14 @@ +// Common/CRC.cpp + +#include "StdAfx.h" + +extern "C" +{ +#include "../../C/7zCrc.h" +} + +class CCRCTableInit +{ +public: + CCRCTableInit() { CrcGenerateTable(); } +} g_CRCTableInit; diff --git a/3rdparty/physfs/lzma/CPP/Common/C_FileIO.cpp b/3rdparty/physfs/lzma/CPP/Common/C_FileIO.cpp new file mode 100644 index 0000000..3c7f82d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/C_FileIO.cpp @@ -0,0 +1,88 @@ +// Common/C_FileIO.h + +#include "C_FileIO.h" + +#include +#include + +namespace NC { +namespace NFile { +namespace NIO { + +bool CFileBase::OpenBinary(const char *name, int flags) +{ + #ifdef O_BINARY + flags |= O_BINARY; + #endif + Close(); + _handle = ::open(name, flags, 0666); + return _handle != -1; +} + +bool CFileBase::Close() +{ + if(_handle == -1) + return true; + if (close(_handle) != 0) + return false; + _handle = -1; + return true; +} + +bool CFileBase::GetLength(UInt64 &length) const +{ + off_t curPos = Seek(0, SEEK_CUR); + off_t lengthTemp = Seek(0, SEEK_END); + Seek(curPos, SEEK_SET); + length = (UInt64)lengthTemp; + return true; +} + +off_t CFileBase::Seek(off_t distanceToMove, int moveMethod) const +{ + return ::lseek(_handle, distanceToMove, moveMethod); +} + +///////////////////////// +// CInFile + +bool CInFile::Open(const char *name) +{ + return CFileBase::OpenBinary(name, O_RDONLY); +} + +bool CInFile::OpenShared(const char *name, bool) +{ + return Open(name); +} + +ssize_t CInFile::Read(void *data, size_t size) +{ + return read(_handle, data, size); +} + +///////////////////////// +// COutFile + +bool COutFile::Create(const char *name, bool createAlways) +{ + if (createAlways) + { + Close(); + _handle = ::creat(name, 0666); + return _handle != -1; + } + return OpenBinary(name, O_CREAT | O_EXCL | O_WRONLY); +} + +bool COutFile::Open(const char *name, DWORD creationDisposition) +{ + return Create(name, false); +} + +ssize_t COutFile::Write(const void *data, size_t size) +{ + return write(_handle, data, size); +} + +}}} diff --git a/3rdparty/physfs/lzma/CPP/Common/C_FileIO.h b/3rdparty/physfs/lzma/CPP/Common/C_FileIO.h new file mode 100644 index 0000000..27aa568 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/C_FileIO.h @@ -0,0 +1,47 @@ +// Common/C_FileIO.h + +#ifndef __COMMON_C_FILEIO_H +#define __COMMON_C_FILEIO_H + +#include +#include + +#include "Types.h" +#include "MyWindows.h" + +namespace NC { +namespace NFile { +namespace NIO { + +class CFileBase +{ +protected: + int _handle; + bool OpenBinary(const char *name, int flags); +public: + CFileBase(): _handle(-1) {}; + ~CFileBase() { Close(); } + bool Close(); + bool GetLength(UInt64 &length) const; + off_t Seek(off_t distanceToMove, int moveMethod) const; +}; + +class CInFile: public CFileBase +{ +public: + bool Open(const char *name); + bool OpenShared(const char *name, bool shareForWrite); + ssize_t Read(void *data, size_t size); +}; + +class COutFile: public CFileBase +{ +public: + bool Create(const char *name, bool createAlways); + bool Open(const char *name, DWORD creationDisposition); + ssize_t Write(const void *data, size_t size); +}; + +}}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/ComTry.h b/3rdparty/physfs/lzma/CPP/Common/ComTry.h new file mode 100644 index 0000000..5153362 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/ComTry.h @@ -0,0 +1,17 @@ +// ComTry.h + +#ifndef __COM_TRY_H +#define __COM_TRY_H + +#include "MyWindows.h" +// #include "Exception.h" +// #include "NewHandler.h" + +#define COM_TRY_BEGIN try { +#define COM_TRY_END } catch(...) { return E_OUTOFMEMORY; } + + // catch(const CNewException &) { return E_OUTOFMEMORY; }\ + // catch(const CSystemException &e) { return e.ErrorCode; }\ + // catch(...) { return E_FAIL; } + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.cpp b/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.cpp new file mode 100644 index 0000000..67f7267 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.cpp @@ -0,0 +1,232 @@ +// CommandLineParser.cpp + +#include "StdAfx.h" + +#include "CommandLineParser.h" + +namespace NCommandLineParser { + +void SplitCommandLine(const UString &src, UString &dest1, UString &dest2) +{ + dest1.Empty(); + dest2.Empty(); + bool quoteMode = false; + int i; + for (i = 0; i < src.Length(); i++) + { + wchar_t c = src[i]; + if (c == L'\"') + quoteMode = !quoteMode; + else if (c == L' ' && !quoteMode) + { + i++; + break; + } + else + dest1 += c; + } + dest2 = src.Mid(i); +} + +void SplitCommandLine(const UString &s, UStringVector &parts) +{ + UString sTemp = s; + sTemp.Trim(); + parts.Clear(); + for (;;) + { + UString s1, s2; + SplitCommandLine(sTemp, s1, s2); + // s1.Trim(); + // s2.Trim(); + if (!s1.IsEmpty()) + parts.Add(s1); + if (s2.IsEmpty()) + break; + sTemp = s2; + } +} + + +static const wchar_t kSwitchID1 = '-'; +// static const wchar_t kSwitchID2 = '/'; + +static const wchar_t kSwitchMinus = '-'; +static const wchar_t *kStopSwitchParsing = L"--"; + +static bool IsItSwitchChar(wchar_t c) +{ + return (c == kSwitchID1 /*|| c == kSwitchID2 */); +} + +CParser::CParser(int numSwitches): + _numSwitches(numSwitches) +{ + _switches = new CSwitchResult[_numSwitches]; +} + +CParser::~CParser() +{ + delete []_switches; +} + +void CParser::ParseStrings(const CSwitchForm *switchForms, + const UStringVector &commandStrings) +{ + int numCommandStrings = commandStrings.Size(); + bool stopSwitch = false; + for (int i = 0; i < numCommandStrings; i++) + { + const UString &s = commandStrings[i]; + if (stopSwitch) + NonSwitchStrings.Add(s); + else + if (s == kStopSwitchParsing) + stopSwitch = true; + else + if (!ParseString(s, switchForms)) + NonSwitchStrings.Add(s); + } +} + +// if string contains switch then function updates switch structures +// out: (string is a switch) +bool CParser::ParseString(const UString &s, const CSwitchForm *switchForms) +{ + int len = s.Length(); + if (len == 0) + return false; + int pos = 0; + if (!IsItSwitchChar(s[pos])) + return false; + while(pos < len) + { + if (IsItSwitchChar(s[pos])) + pos++; + const int kNoLen = -1; + int matchedSwitchIndex = 0; // GCC Warning + int maxLen = kNoLen; + for(int switchIndex = 0; switchIndex < _numSwitches; switchIndex++) + { + int switchLen = MyStringLen(switchForms[switchIndex].IDString); + if (switchLen <= maxLen || pos + switchLen > len) + continue; + + UString temp = s + pos; + temp = temp.Left(switchLen); + if(temp.CompareNoCase(switchForms[switchIndex].IDString) == 0) + // if(_strnicmp(switchForms[switchIndex].IDString, LPCSTR(s) + pos, switchLen) == 0) + { + matchedSwitchIndex = switchIndex; + maxLen = switchLen; + } + } + if (maxLen == kNoLen) + throw "maxLen == kNoLen"; + CSwitchResult &matchedSwitch = _switches[matchedSwitchIndex]; + const CSwitchForm &switchForm = switchForms[matchedSwitchIndex]; + if ((!switchForm.Multi) && matchedSwitch.ThereIs) + throw "switch must be single"; + matchedSwitch.ThereIs = true; + pos += maxLen; + int tailSize = len - pos; + NSwitchType::EEnum type = switchForm.Type; + switch(type) + { + case NSwitchType::kPostMinus: + { + if (tailSize == 0) + matchedSwitch.WithMinus = false; + else + { + matchedSwitch.WithMinus = (s[pos] == kSwitchMinus); + if (matchedSwitch.WithMinus) + pos++; + } + break; + } + case NSwitchType::kPostChar: + { + if (tailSize < switchForm.MinLen) + throw "switch is not full"; + UString set = switchForm.PostCharSet; + const int kEmptyCharValue = -1; + if (tailSize == 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + int index = set.Find(s[pos]); + if (index < 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + matchedSwitch.PostCharIndex = index; + pos++; + } + } + break; + } + case NSwitchType::kLimitedPostString: + case NSwitchType::kUnLimitedPostString: + { + int minLen = switchForm.MinLen; + if (tailSize < minLen) + throw "switch is not full"; + if (type == NSwitchType::kUnLimitedPostString) + { + matchedSwitch.PostStrings.Add(s.Mid(pos)); + return true; + } + int maxLen = switchForm.MaxLen; + UString stringSwitch = s.Mid(pos, minLen); + pos += minLen; + for(int i = minLen; i < maxLen && pos < len; i++, pos++) + { + wchar_t c = s[pos]; + if (IsItSwitchChar(c)) + break; + stringSwitch += c; + } + matchedSwitch.PostStrings.Add(stringSwitch); + break; + } + case NSwitchType::kSimple: + break; + } + } + return true; +} + +const CSwitchResult& CParser::operator[](size_t index) const +{ + return _switches[index]; +} + +///////////////////////////////// +// Command parsing procedures + +int ParseCommand(int numCommandForms, const CCommandForm *commandForms, + const UString &commandString, UString &postString) +{ + for(int i = 0; i < numCommandForms; i++) + { + const UString id = commandForms[i].IDString; + if (commandForms[i].PostStringMode) + { + if(commandString.Find(id) == 0) + { + postString = commandString.Mid(id.Length()); + return i; + } + } + else + if (commandString == id) + { + postString.Empty(); + return i; + } + } + return -1; +} + +} diff --git a/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.h b/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.h new file mode 100644 index 0000000..a97f58a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/CommandLineParser.h @@ -0,0 +1,72 @@ +// Common/CommandLineParser.h + +#ifndef __COMMON_COMMANDLINEPARSER_H +#define __COMMON_COMMANDLINEPARSER_H + +#include "MyString.h" + +namespace NCommandLineParser { + +void SplitCommandLine(const UString &src, UString &dest1, UString &dest2); +void SplitCommandLine(const UString &s, UStringVector &parts); + +namespace NSwitchType { + enum EEnum + { + kSimple, + kPostMinus, + kLimitedPostString, + kUnLimitedPostString, + kPostChar + }; +} + +struct CSwitchForm +{ + const wchar_t *IDString; + NSwitchType::EEnum Type; + bool Multi; + int MinLen; + int MaxLen; + const wchar_t *PostCharSet; +}; + +struct CSwitchResult +{ + bool ThereIs; + bool WithMinus; + UStringVector PostStrings; + int PostCharIndex; + CSwitchResult(): ThereIs(false) {}; +}; + +class CParser +{ + int _numSwitches; + CSwitchResult *_switches; + bool ParseString(const UString &s, const CSwitchForm *switchForms); +public: + UStringVector NonSwitchStrings; + CParser(int numSwitches); + ~CParser(); + void ParseStrings(const CSwitchForm *switchForms, + const UStringVector &commandStrings); + const CSwitchResult& operator[](size_t index) const; +}; + +///////////////////////////////// +// Command parsing procedures + +struct CCommandForm +{ + wchar_t *IDString; + bool PostStringMode; +}; + +// Returns: Index of form and postString; -1, if there is no match +int ParseCommand(int numCommandForms, const CCommandForm *commandForms, + const UString &commandString, UString &postString); + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/Defs.h b/3rdparty/physfs/lzma/CPP/Common/Defs.h new file mode 100644 index 0000000..dad3ae8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/Defs.h @@ -0,0 +1,20 @@ +// Common/Defs.h + +#ifndef __COMMON_DEFS_H +#define __COMMON_DEFS_H + +template inline T MyMin(T a, T b) + { return a < b ? a : b; } +template inline T MyMax(T a, T b) + { return a > b ? a : b; } + +template inline int MyCompare(T a, T b) + { return a < b ? -1 : (a == b ? 0 : 1); } + +inline int BoolToInt(bool value) + { return (value ? 1: 0); } + +inline bool IntToBool(int value) + { return (value != 0); } + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/DynamicBuffer.h b/3rdparty/physfs/lzma/CPP/Common/DynamicBuffer.h new file mode 100644 index 0000000..1709657 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/DynamicBuffer.h @@ -0,0 +1,47 @@ +// Common/DynamicBuffer.h + +#ifndef __COMMON_DYNAMICBUFFER_H +#define __COMMON_DYNAMICBUFFER_H + +#include "Buffer.h" + +template class CDynamicBuffer: public CBuffer +{ + void GrowLength(size_t size) + { + size_t delta; + if (this->_capacity > 64) + delta = this->_capacity / 4; + else if (this->_capacity > 8) + delta = 16; + else + delta = 4; + delta = MyMax(delta, size); + SetCapacity(this->_capacity + delta); + } +public: + CDynamicBuffer(): CBuffer() {}; + CDynamicBuffer(const CDynamicBuffer &buffer): CBuffer(buffer) {}; + CDynamicBuffer(size_t size): CBuffer(size) {}; + CDynamicBuffer& operator=(const CDynamicBuffer &buffer) + { + this->Free(); + if(buffer._capacity > 0) + { + SetCapacity(buffer._capacity); + memmove(this->_items, buffer._items, buffer._capacity * sizeof(T)); + } + return *this; + } + void EnsureCapacity(size_t capacity) + { + if (this->_capacity < capacity) + GrowLength(capacity - this->_capacity); + } +}; + +typedef CDynamicBuffer CCharDynamicBuffer; +typedef CDynamicBuffer CWCharDynamicBuffer; +typedef CDynamicBuffer CByteDynamicBuffer; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/IntToString.cpp b/3rdparty/physfs/lzma/CPP/Common/IntToString.cpp new file mode 100644 index 0000000..340d865 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/IntToString.cpp @@ -0,0 +1,63 @@ +// Common/IntToString.cpp + +#include "StdAfx.h" + +#include "IntToString.h" + +void ConvertUInt64ToString(UInt64 value, char *s, UInt32 base) +{ + if (base < 2 || base > 36) + { + *s = '\0'; + return; + } + char temp[72]; + int pos = 0; + do + { + int delta = (int)(value % base); + temp[pos++] = (char)((delta < 10) ? ('0' + delta) : ('a' + (delta - 10))); + value /= base; + } + while (value != 0); + do + *s++ = temp[--pos]; + while(pos > 0); + *s = '\0'; +} + +void ConvertUInt64ToString(UInt64 value, wchar_t *s) +{ + wchar_t temp[32]; + int pos = 0; + do + { + temp[pos++] = (wchar_t)(L'0' + (int)(value % 10)); + value /= 10; + } + while (value != 0); + do + *s++ = temp[--pos]; + while(pos > 0); + *s = L'\0'; +} + +void ConvertInt64ToString(Int64 value, char *s) +{ + if (value < 0) + { + *s++ = '-'; + value = -value; + } + ConvertUInt64ToString(value, s); +} + +void ConvertInt64ToString(Int64 value, wchar_t *s) +{ + if (value < 0) + { + *s++ = L'-'; + value = -value; + } + ConvertUInt64ToString(value, s); +} diff --git a/3rdparty/physfs/lzma/CPP/Common/IntToString.h b/3rdparty/physfs/lzma/CPP/Common/IntToString.h new file mode 100644 index 0000000..cf86090 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/IntToString.h @@ -0,0 +1,15 @@ +// Common/IntToString.h + +#ifndef __COMMON_INTTOSTRING_H +#define __COMMON_INTTOSTRING_H + +#include +#include "Types.h" + +void ConvertUInt64ToString(UInt64 value, char *s, UInt32 base = 10); +void ConvertUInt64ToString(UInt64 value, wchar_t *s); + +void ConvertInt64ToString(Int64 value, char *s); +void ConvertInt64ToString(Int64 value, wchar_t *s); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.cpp b/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.cpp new file mode 100644 index 0000000..4f8a9e5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.cpp @@ -0,0 +1,74 @@ +// Common/ListFileUtils.cpp + +#include "StdAfx.h" + +#include "../Windows/FileIO.h" + +#include "ListFileUtils.h" +#include "StringConvert.h" +#include "UTFConvert.h" + +static const char kQuoteChar = '\"'; +static void RemoveQuote(UString &s) +{ + if (s.Length() >= 2) + if (s[0] == kQuoteChar && s[s.Length() - 1] == kQuoteChar) + s = s.Mid(1, s.Length() - 2); +} + +bool ReadNamesFromListFile(LPCWSTR fileName, UStringVector &resultStrings, UINT codePage) +{ + NWindows::NFile::NIO::CInFile file; + if (!file.Open(fileName)) + return false; + UInt64 length; + if (!file.GetLength(length)) + return false; + if (length > ((UInt32)1 << 31)) + return false; + AString s; + char *p = s.GetBuffer((int)length + 1); + UInt32 processed; + if (!file.Read(p, (UInt32)length, processed)) + return false; + p[(UInt32)length] = 0; + s.ReleaseBuffer(); + file.Close(); + + UString u; + #ifdef CP_UTF8 + if (codePage == CP_UTF8) + { + if (!ConvertUTF8ToUnicode(s, u)) + return false; + } + else + #endif + u = MultiByteToUnicodeString(s, codePage); + if (!u.IsEmpty()) + { + if (u[0] == 0xFEFF) + u.Delete(0); + } + + UString t; + for(int i = 0; i < u.Length(); i++) + { + wchar_t c = u[i]; + if (c == L'\n' || c == 0xD) + { + t.Trim(); + RemoveQuote(t); + if (!t.IsEmpty()) + resultStrings.Add(t); + t.Empty(); + } + else + t += c; + } + t.Trim(); + RemoveQuote(t); + if (!t.IsEmpty()) + resultStrings.Add(t); + return true; +} diff --git a/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.h b/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.h new file mode 100644 index 0000000..c58a8bd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/ListFileUtils.h @@ -0,0 +1,11 @@ +// Common/ListFileUtils.h + +#ifndef __COMMON_LISTFILEUTILS_H +#define __COMMON_LISTFILEUTILS_H + +#include "MyString.h" +#include "Types.h" + +bool ReadNamesFromListFile(LPCWSTR fileName, UStringVector &strings, UINT codePage = CP_OEMCP); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyCom.h b/3rdparty/physfs/lzma/CPP/Common/MyCom.h new file mode 100644 index 0000000..dcc94f1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyCom.h @@ -0,0 +1,218 @@ +// MyCom.h + +#ifndef __MYCOM_H +#define __MYCOM_H + +#include "MyWindows.h" + +#ifndef RINOK +#define RINOK(x) { HRESULT __result_ = (x); if(__result_ != S_OK) return __result_; } +#endif + +template +class CMyComPtr +{ + T* _p; +public: + // typedef T _PtrClass; + CMyComPtr() { _p = NULL;} + CMyComPtr(T* p) {if ((_p = p) != NULL) p->AddRef(); } + CMyComPtr(const CMyComPtr& lp) + { + if ((_p = lp._p) != NULL) + _p->AddRef(); + } + ~CMyComPtr() { if (_p) _p->Release(); } + void Release() { if (_p) { _p->Release(); _p = NULL; } } + operator T*() const { return (T*)_p; } + // T& operator*() const { return *_p; } + T** operator&() { return &_p; } + T* operator->() const { return _p; } + T* operator=(T* p) + { + if (p != 0) + p->AddRef(); + if (_p) + _p->Release(); + _p = p; + return p; + } + T* operator=(const CMyComPtr& lp) { return (*this = lp._p); } + bool operator!() const { return (_p == NULL); } + // bool operator==(T* pT) const { return _p == pT; } + // Compare two objects for equivalence + void Attach(T* p2) + { + Release(); + _p = p2; + } + T* Detach() + { + T* pt = _p; + _p = NULL; + return pt; + } + #ifdef _WIN32 + HRESULT CoCreateInstance(REFCLSID rclsid, REFIID iid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) + { + return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, (void**)&_p); + } + #endif + /* + HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) + { + CLSID clsid; + HRESULT hr = CLSIDFromProgID(szProgID, &clsid); + ATLASSERT(_p == NULL); + if (SUCCEEDED(hr)) + hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&_p); + return hr; + } + */ + template + HRESULT QueryInterface(REFGUID iid, Q** pp) const + { + return _p->QueryInterface(iid, (void**)pp); + } +}; + +////////////////////////////////////////////////////////// + +class CMyComBSTR +{ +public: + BSTR m_str; + CMyComBSTR() { m_str = NULL; } + CMyComBSTR(LPCOLESTR pSrc) { m_str = ::SysAllocString(pSrc); } + // CMyComBSTR(int nSize) { m_str = ::SysAllocStringLen(NULL, nSize); } + // CMyComBSTR(int nSize, LPCOLESTR sz) { m_str = ::SysAllocStringLen(sz, nSize); } + CMyComBSTR(const CMyComBSTR& src) { m_str = src.MyCopy(); } + /* + CMyComBSTR(REFGUID src) + { + LPOLESTR szGuid; + StringFromCLSID(src, &szGuid); + m_str = ::SysAllocString(szGuid); + CoTaskMemFree(szGuid); + } + */ + ~CMyComBSTR() { ::SysFreeString(m_str); } + CMyComBSTR& operator=(const CMyComBSTR& src) + { + if (m_str != src.m_str) + { + if (m_str) + ::SysFreeString(m_str); + m_str = src.MyCopy(); + } + return *this; + } + CMyComBSTR& operator=(LPCOLESTR pSrc) + { + ::SysFreeString(m_str); + m_str = ::SysAllocString(pSrc); + return *this; + } + unsigned int Length() const { return ::SysStringLen(m_str); } + operator BSTR() const { return m_str; } + BSTR* operator&() { return &m_str; } + BSTR MyCopy() const + { + int byteLen = ::SysStringByteLen(m_str); + BSTR res = ::SysAllocStringByteLen(NULL, byteLen); + memmove(res, m_str, byteLen); + return res; + } + void Attach(BSTR src) { m_str = src; } + BSTR Detach() + { + BSTR s = m_str; + m_str = NULL; + return s; + } + void Empty() + { + ::SysFreeString(m_str); + m_str = NULL; + } + bool operator!() const { return (m_str == NULL); } +}; + + +////////////////////////////////////////////////////////// + +class CMyUnknownImp +{ +public: + ULONG __m_RefCount; + CMyUnknownImp(): __m_RefCount(0) {} +}; + +#define MY_QUERYINTERFACE_BEGIN STDMETHOD(QueryInterface) \ + (REFGUID iid, void **outObject) { + +#define MY_QUERYINTERFACE_ENTRY(i) if (iid == IID_ ## i) \ + { *outObject = (void *)(i *)this; AddRef(); return S_OK; } + +#define MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) if (iid == IID_IUnknown) \ + { *outObject = (void *)(IUnknown *)(i *)this; AddRef(); return S_OK; } + +#define MY_QUERYINTERFACE_BEGIN2(i) MY_QUERYINTERFACE_BEGIN \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ + MY_QUERYINTERFACE_ENTRY(i) + +#define MY_QUERYINTERFACE_END return E_NOINTERFACE; } + +#define MY_ADDREF_RELEASE \ +STDMETHOD_(ULONG, AddRef)() { return ++__m_RefCount; } \ +STDMETHOD_(ULONG, Release)() { if (--__m_RefCount != 0) \ + return __m_RefCount; delete this; return 0; } + +#define MY_UNKNOWN_IMP_SPEC(i) \ + MY_QUERYINTERFACE_BEGIN \ + i \ + MY_QUERYINTERFACE_END \ + MY_ADDREF_RELEASE + + +#define MY_UNKNOWN_IMP MY_QUERYINTERFACE_BEGIN \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(IUnknown) \ + MY_QUERYINTERFACE_END \ + MY_ADDREF_RELEASE + +#define MY_UNKNOWN_IMP1(i) MY_UNKNOWN_IMP_SPEC( \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ + MY_QUERYINTERFACE_ENTRY(i) \ + ) + +#define MY_UNKNOWN_IMP2(i1, i2) MY_UNKNOWN_IMP_SPEC( \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ + MY_QUERYINTERFACE_ENTRY(i1) \ + MY_QUERYINTERFACE_ENTRY(i2) \ + ) + +#define MY_UNKNOWN_IMP3(i1, i2, i3) MY_UNKNOWN_IMP_SPEC( \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ + MY_QUERYINTERFACE_ENTRY(i1) \ + MY_QUERYINTERFACE_ENTRY(i2) \ + MY_QUERYINTERFACE_ENTRY(i3) \ + ) + +#define MY_UNKNOWN_IMP4(i1, i2, i3, i4) MY_UNKNOWN_IMP_SPEC( \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ + MY_QUERYINTERFACE_ENTRY(i1) \ + MY_QUERYINTERFACE_ENTRY(i2) \ + MY_QUERYINTERFACE_ENTRY(i3) \ + MY_QUERYINTERFACE_ENTRY(i4) \ + ) + +#define MY_UNKNOWN_IMP5(i1, i2, i3, i4, i5) MY_UNKNOWN_IMP_SPEC( \ + MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ + MY_QUERYINTERFACE_ENTRY(i1) \ + MY_QUERYINTERFACE_ENTRY(i2) \ + MY_QUERYINTERFACE_ENTRY(i3) \ + MY_QUERYINTERFACE_ENTRY(i4) \ + MY_QUERYINTERFACE_ENTRY(i5) \ + ) + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyException.h b/3rdparty/physfs/lzma/CPP/Common/MyException.h new file mode 100644 index 0000000..f0ad111 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyException.h @@ -0,0 +1,14 @@ +// Common/Exception.h + +#ifndef __COMMON_EXCEPTION_H +#define __COMMON_EXCEPTION_H + +#include "MyWindows.h" + +struct CSystemException +{ + HRESULT ErrorCode; + CSystemException(HRESULT errorCode): ErrorCode(errorCode) {} +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyGuidDef.h b/3rdparty/physfs/lzma/CPP/Common/MyGuidDef.h new file mode 100644 index 0000000..1956269 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyGuidDef.h @@ -0,0 +1,54 @@ +// Common/MyGuidDef.h + +#ifndef GUID_DEFINED +#define GUID_DEFINED + +#include "Types.h" + +typedef struct { + UInt32 Data1; + UInt16 Data2; + UInt16 Data3; + unsigned char Data4[8]; +} GUID; + +#ifdef __cplusplus +#define REFGUID const GUID & +#else +#define REFGUID const GUID * +#endif + +#define REFCLSID REFGUID +#define REFIID REFGUID + +#ifdef __cplusplus +inline int operator==(REFGUID g1, REFGUID g2) +{ + for (int i = 0; i < (int)sizeof(g1); i++) + if (((unsigned char *)&g1)[i] != ((unsigned char *)&g2)[i]) + return 0; + return 1; +} +inline int operator!=(REFGUID g1, REFGUID g2) { return !(g1 == g2); } +#endif + +#ifdef __cplusplus + #define MY_EXTERN_C extern "C" +#else + #define MY_EXTERN_C extern +#endif + +#endif // GUID_DEFINED + + +#ifdef DEFINE_GUID +#undef DEFINE_GUID +#endif + +#ifdef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + MY_EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + MY_EXTERN_C const GUID name +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyInitGuid.h b/3rdparty/physfs/lzma/CPP/Common/MyInitGuid.h new file mode 100644 index 0000000..4fc1556 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyInitGuid.h @@ -0,0 +1,15 @@ +// Common/MyInitGuid.h + +#ifndef __COMMON_MYINITGUID_H +#define __COMMON_MYINITGUID_H + +#ifdef _WIN32 +#include +#else +#define INITGUID +#include "MyGuidDef.h" +DEFINE_GUID(IID_IUnknown, +0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyString.cpp b/3rdparty/physfs/lzma/CPP/Common/MyString.cpp new file mode 100644 index 0000000..affdcb0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyString.cpp @@ -0,0 +1,198 @@ +// Common/MyString.cpp + +#include "StdAfx.h" + +#ifdef _WIN32 +#include "StringConvert.h" +#else +#include +#endif + +#include "MyString.h" + + +#ifdef _WIN32 + +#ifndef _UNICODE + +wchar_t MyCharUpper(wchar_t c) +{ + if (c == 0) + return 0; + wchar_t *res = CharUpperW((LPWSTR)(UINT_PTR)(unsigned int)c); + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return (wchar_t)(unsigned int)(UINT_PTR)res; + const int kBufferSize = 4; + char s[kBufferSize + 1]; + int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufferSize, 0, 0); + if (numChars == 0 || numChars > kBufferSize) + return c; + s[numChars] = 0; + ::CharUpperA(s); + ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); + return c; +} + +wchar_t MyCharLower(wchar_t c) +{ + if (c == 0) + return 0; + wchar_t *res = CharLowerW((LPWSTR)(UINT_PTR)(unsigned int)c); + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return (wchar_t)(unsigned int)(UINT_PTR)res; + const int kBufferSize = 4; + char s[kBufferSize + 1]; + int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufferSize, 0, 0); + if (numChars == 0 || numChars > kBufferSize) + return c; + s[numChars] = 0; + ::CharLowerA(s); + ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); + return c; +} + +wchar_t * MyStringUpper(wchar_t *s) +{ + if (s == 0) + return 0; + wchar_t *res = CharUpperW(s); + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return res; + AString a = UnicodeStringToMultiByte(s); + a.MakeUpper(); + return MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); +} + +wchar_t * MyStringLower(wchar_t *s) +{ + if (s == 0) + return 0; + wchar_t *res = CharLowerW(s); + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return res; + AString a = UnicodeStringToMultiByte(s); + a.MakeLower(); + return MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); +} + +#endif + +/* +inline int ConvertCompareResult(int r) { return r - 2; } + +int MyStringCollate(const wchar_t *s1, const wchar_t *s2) +{ + int res = CompareStringW( + LOCALE_USER_DEFAULT, SORT_STRINGSORT, s1, -1, s2, -1); + #ifdef _UNICODE + return ConvertCompareResult(res); + #else + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return ConvertCompareResult(res); + return MyStringCollate(UnicodeStringToMultiByte(s1), + UnicodeStringToMultiByte(s2)); + #endif +} + +#ifndef _WIN32_WCE +int MyStringCollate(const char *s1, const char *s2) +{ + return ConvertCompareResult(CompareStringA( + LOCALE_USER_DEFAULT, SORT_STRINGSORT, s1, -1, s2, -1)); +} + +int MyStringCollateNoCase(const char *s1, const char *s2) +{ + return ConvertCompareResult(CompareStringA( + LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, s1, -1, s2, -1)); +} +#endif + +int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2) +{ + int res = CompareStringW( + LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, s1, -1, s2, -1); + #ifdef _UNICODE + return ConvertCompareResult(res); + #else + if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return ConvertCompareResult(res); + return MyStringCollateNoCase(UnicodeStringToMultiByte(s1), + UnicodeStringToMultiByte(s2)); + #endif +} +*/ + +#else + +wchar_t MyCharUpper(wchar_t c) +{ + return toupper(c); +} + +/* +int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2) +{ + for (;;) + { + wchar_t c1 = *s1++; + wchar_t c2 = *s2++; + wchar_t u1 = MyCharUpper(c1); + wchar_t u2 = MyCharUpper(c2); + + if (u1 < u2) return -1; + if (u1 > u2) return 1; + if (u1 == 0) return 0; + } +} +*/ + +#endif + +int MyStringCompare(const char *s1, const char *s2) +{ + for (;;) + { + unsigned char c1 = (unsigned char)*s1++; + unsigned char c2 = (unsigned char)*s2++; + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} + +int MyStringCompare(const wchar_t *s1, const wchar_t *s2) +{ + for (;;) + { + wchar_t c1 = *s1++; + wchar_t c2 = *s2++; + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} + +int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2) +{ + for (;;) + { + wchar_t c1 = *s1++; + wchar_t c2 = *s2++; + if (c1 != c2) + { + wchar_t u1 = MyCharUpper(c1); + wchar_t u2 = MyCharUpper(c2); + if (u1 < u2) return -1; + if (u1 > u2) return 1; + } + if (c1 == 0) return 0; + } +} + +#ifdef _WIN32 +int MyStringCompareNoCase(const char *s1, const char *s2) +{ + return MyStringCompareNoCase(MultiByteToUnicodeString(s1), MultiByteToUnicodeString(s2)); +} +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyString.h b/3rdparty/physfs/lzma/CPP/Common/MyString.h new file mode 100644 index 0000000..c46ca54 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyString.h @@ -0,0 +1,636 @@ +// Common/String.h + +#ifndef __COMMON_STRING_H +#define __COMMON_STRING_H + +#include +// #include + +#include "MyVector.h" + +#ifdef _WIN32 +#include "MyWindows.h" +#endif + +template +inline int MyStringLen(const T *s) +{ + int i; + for (i = 0; s[i] != '\0'; i++); + return i; +} + +template +inline T * MyStringCopy(T *dest, const T *src) +{ + T *destStart = dest; + while((*dest++ = *src++) != 0); + return destStart; +} + +inline wchar_t* MyStringGetNextCharPointer(wchar_t *p) + { return (p + 1); } +inline const wchar_t* MyStringGetNextCharPointer(const wchar_t *p) + { return (p + 1); } +inline wchar_t* MyStringGetPrevCharPointer(const wchar_t *, wchar_t *p) + { return (p - 1); } +inline const wchar_t* MyStringGetPrevCharPointer(const wchar_t *, const wchar_t *p) + { return (p - 1); } + +#ifdef _WIN32 + +inline char* MyStringGetNextCharPointer(char *p) + { return CharNextA(p); } +inline const char* MyStringGetNextCharPointer(const char *p) + { return CharNextA(p); } + +inline char* MyStringGetPrevCharPointer(char *base, char *p) + { return CharPrevA(base, p); } +inline const char* MyStringGetPrevCharPointer(const char *base, const char *p) + { return CharPrevA(base, p); } + +inline char MyCharUpper(char c) + { return (char)(unsigned int)(UINT_PTR)CharUpperA((LPSTR)(UINT_PTR)(unsigned int)(unsigned char)c); } +#ifdef _UNICODE +inline wchar_t MyCharUpper(wchar_t c) + { return (wchar_t)(unsigned int)(UINT_PTR)CharUpperW((LPWSTR)(UINT_PTR)(unsigned int)c); } +#else +wchar_t MyCharUpper(wchar_t c); +#endif + +inline char MyCharLower(char c) + { return (char)(unsigned int)(UINT_PTR)CharLowerA((LPSTR)(UINT_PTR)(unsigned int)(unsigned char)c); } +#ifdef _UNICODE +inline wchar_t MyCharLower(wchar_t c) + { return (wchar_t)(unsigned int)(UINT_PTR)CharLowerW((LPWSTR)(UINT_PTR)(unsigned int)c); } +#else +wchar_t MyCharLower(wchar_t c); +#endif + +inline char * MyStringUpper(char *s) { return CharUpperA(s); } +#ifdef _UNICODE +inline wchar_t * MyStringUpper(wchar_t *s) { return CharUpperW(s); } +#else +wchar_t * MyStringUpper(wchar_t *s); +#endif + +inline char * MyStringLower(char *s) { return CharLowerA(s); } +#ifdef _UNICODE +inline wchar_t * MyStringLower(wchar_t *s) { return CharLowerW(s); } +#else +wchar_t * MyStringLower(wchar_t *s); +#endif + +#else // Standard-C +wchar_t MyCharUpper(wchar_t c); +#endif + +////////////////////////////////////// +// Compare + +/* +#ifndef _WIN32_WCE +int MyStringCollate(const char *s1, const char *s2); +int MyStringCollateNoCase(const char *s1, const char *s2); +#endif +int MyStringCollate(const wchar_t *s1, const wchar_t *s2); +int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2); +*/ + +int MyStringCompare(const char *s1, const char *s2); +int MyStringCompare(const wchar_t *s1, const wchar_t *s2); + +#ifdef _WIN32 +int MyStringCompareNoCase(const char *s1, const char *s2); +#endif + +int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2); + +template +class CStringBase +{ + void TrimLeftWithCharSet(const CStringBase &charSet) + { + const T *p = _chars; + while (charSet.Find(*p) >= 0 && (*p != 0)) + p = GetNextCharPointer(p); + Delete(0, (int)(p - _chars)); + } + void TrimRightWithCharSet(const CStringBase &charSet) + { + const T *p = _chars; + const T *pLast = NULL; + while (*p != 0) + { + if (charSet.Find(*p) >= 0) + { + if (pLast == NULL) + pLast = p; + } + else + pLast = NULL; + p = GetNextCharPointer(p); + } + if(pLast != NULL) + { + int i = (int)(pLast - _chars); + Delete(i, _length - i); + } + + } + void MoveItems(int destIndex, int srcIndex) + { + memmove(_chars + destIndex, _chars + srcIndex, + sizeof(T) * (_length - srcIndex + 1)); + } + + void InsertSpace(int &index, int size) + { + CorrectIndex(index); + GrowLength(size); + MoveItems(index + size, index); + } + + static T *GetNextCharPointer(T *p) + { return MyStringGetNextCharPointer(p); } + static const T *GetNextCharPointer(const T *p) + { return MyStringGetNextCharPointer(p); } + static T *GetPrevCharPointer(T *base, T *p) + { return MyStringGetPrevCharPointer(base, p); } + static const T *GetPrevCharPointer(const T *base, const T *p) + { return MyStringGetPrevCharPointer(base, p); } +protected: + T *_chars; + int _length; + int _capacity; + + void SetCapacity(int newCapacity) + { + int realCapacity = newCapacity + 1; + if(realCapacity == _capacity) + return; + /* + const int kMaxStringSize = 0x20000000; + #ifndef _WIN32_WCE + if(newCapacity > kMaxStringSize || newCapacity < _length) + throw 1052337; + #endif + */ + T *newBuffer = new T[realCapacity]; + if(_capacity > 0) + { + for (int i = 0; i < (_length + 1); i++) + newBuffer[i] = _chars[i]; + delete []_chars; + _chars = newBuffer; + } + else + { + _chars = newBuffer; + _chars[0] = 0; + } + _capacity = realCapacity; + } + + void GrowLength(int n) + { + int freeSize = _capacity - _length - 1; + if (n <= freeSize) + return; + int delta; + if (_capacity > 64) + delta = _capacity / 2; + else if (_capacity > 8) + delta = 16; + else + delta = 4; + if (freeSize + delta < n) + delta = n - freeSize; + SetCapacity(_capacity + delta); + } + + void CorrectIndex(int &index) const + { + if (index > _length) + index = _length; + } + +public: + CStringBase(): _chars(0), _length(0), _capacity(0) + { SetCapacity(16 - 1); } + CStringBase(T c): _chars(0), _length(0), _capacity(0) + { + SetCapacity(1); + _chars[0] = c; + _chars[1] = 0; + _length = 1; + } + CStringBase(const T *chars): _chars(0), _length(0), _capacity(0) + { + int length = MyStringLen(chars); + SetCapacity(length); + MyStringCopy(_chars, chars); // can be optimized by memove() + _length = length; + } + CStringBase(const CStringBase &s): _chars(0), _length(0), _capacity(0) + { + SetCapacity(s._length); + MyStringCopy(_chars, s._chars); + _length = s._length; + } + ~CStringBase() { delete []_chars; } + + operator const T*() const { return _chars;} + + // The minimum size of the character buffer in characters. + // This value does not include space for a null terminator. + T* GetBuffer(int minBufLength) + { + if(minBufLength >= _capacity) + SetCapacity(minBufLength + 1); + return _chars; + } + void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); } + void ReleaseBuffer(int newLength) + { + /* + #ifndef _WIN32_WCE + if(newLength >= _capacity) + throw 282217; + #endif + */ + _chars[newLength] = 0; + _length = newLength; + } + + CStringBase& operator=(T c) + { + Empty(); + SetCapacity(1); + _chars[0] = c; + _chars[1] = 0; + _length = 1; + return *this; + } + CStringBase& operator=(const T *chars) + { + Empty(); + int length = MyStringLen(chars); + SetCapacity(length); + MyStringCopy(_chars, chars); + _length = length; + return *this; + } + CStringBase& operator=(const CStringBase& s) + { + if(&s == this) + return *this; + Empty(); + SetCapacity(s._length); + MyStringCopy(_chars, s._chars); + _length = s._length; + return *this; + } + + CStringBase& operator+=(T c) + { + GrowLength(1); + _chars[_length] = c; + _chars[++_length] = 0; + return *this; + } + CStringBase& operator+=(const T *s) + { + int len = MyStringLen(s); + GrowLength(len); + MyStringCopy(_chars + _length, s); + _length += len; + return *this; + } + CStringBase& operator+=(const CStringBase &s) + { + GrowLength(s._length); + MyStringCopy(_chars + _length, s._chars); + _length += s._length; + return *this; + } + void Empty() + { + _length = 0; + _chars[0] = 0; + } + int Length() const { return _length; } + bool IsEmpty() const { return (_length == 0); } + + CStringBase Mid(int startIndex) const + { return Mid(startIndex, _length - startIndex); } + CStringBase Mid(int startIndex, int count ) const + { + if (startIndex + count > _length) + count = _length - startIndex; + + if (startIndex == 0 && startIndex + count == _length) + return *this; + + CStringBase result; + result.SetCapacity(count); + // MyStringNCopy(result._chars, _chars + startIndex, count); + for (int i = 0; i < count; i++) + result._chars[i] = _chars[startIndex + i]; + result._chars[count] = 0; + result._length = count; + return result; + } + CStringBase Left(int count) const + { return Mid(0, count); } + CStringBase Right(int count) const + { + if (count > _length) + count = _length; + return Mid(_length - count, count); + } + + void MakeUpper() + { MyStringUpper(_chars); } + void MakeLower() + { MyStringLower(_chars); } + + int Compare(const CStringBase& s) const + { return MyStringCompare(_chars, s._chars); } + + int Compare(const T *s) const + { return MyStringCompare(_chars, s); } + + int CompareNoCase(const CStringBase& s) const + { return MyStringCompareNoCase(_chars, s._chars); } + + int CompareNoCase(const T *s) const + { return MyStringCompareNoCase(_chars, s); } + + /* + int Collate(const CStringBase& s) const + { return MyStringCollate(_chars, s._chars); } + int CollateNoCase(const CStringBase& s) const + { return MyStringCollateNoCase(_chars, s._chars); } + */ + + int Find(T c) const { return Find(c, 0); } + int Find(T c, int startIndex) const + { + T *p = _chars + startIndex; + for (;;) + { + if (*p == c) + return (int)(p - _chars); + if (*p == 0) + return -1; + p = GetNextCharPointer(p); + } + } + int Find(const CStringBase &s) const { return Find(s, 0); } + int Find(const CStringBase &s, int startIndex) const + { + if (s.IsEmpty()) + return startIndex; + for (; startIndex < _length; startIndex++) + { + int j; + for (j = 0; j < s._length && startIndex + j < _length; j++) + if (_chars[startIndex+j] != s._chars[j]) + break; + if (j == s._length) + return startIndex; + } + return -1; + } + int ReverseFind(T c) const + { + if (_length == 0) + return -1; + T *p = _chars + _length - 1; + for (;;) + { + if (*p == c) + return (int)(p - _chars); + if (p == _chars) + return -1; + p = GetPrevCharPointer(_chars, p); + } + } + int FindOneOf(const CStringBase &s) const + { + for(int i = 0; i < _length; i++) + if (s.Find(_chars[i]) >= 0) + return i; + return -1; + } + + void TrimLeft(T c) + { + const T *p = _chars; + while (c == *p) + p = GetNextCharPointer(p); + Delete(0, p - _chars); + } + private: + CStringBase GetTrimDefaultCharSet() + { + CStringBase charSet; + charSet += (T)' '; + charSet += (T)'\n'; + charSet += (T)'\t'; + return charSet; + } + public: + + void TrimLeft() + { + TrimLeftWithCharSet(GetTrimDefaultCharSet()); + } + void TrimRight() + { + TrimRightWithCharSet(GetTrimDefaultCharSet()); + } + void TrimRight(T c) + { + const T *p = _chars; + const T *pLast = NULL; + while (*p != 0) + { + if (*p == c) + { + if (pLast == NULL) + pLast = p; + } + else + pLast = NULL; + p = GetNextCharPointer(p); + } + if(pLast != NULL) + { + int i = pLast - _chars; + Delete(i, _length - i); + } + } + void Trim() + { + TrimRight(); + TrimLeft(); + } + + int Insert(int index, T c) + { + InsertSpace(index, 1); + _chars[index] = c; + _length++; + return _length; + } + int Insert(int index, const CStringBase &s) + { + CorrectIndex(index); + if (s.IsEmpty()) + return _length; + int numInsertChars = s.Length(); + InsertSpace(index, numInsertChars); + for(int i = 0; i < numInsertChars; i++) + _chars[index + i] = s[i]; + _length += numInsertChars; + return _length; + } + + // !!!!!!!!!!!!!!! test it if newChar = '\0' + int Replace(T oldChar, T newChar) + { + if (oldChar == newChar) + return 0; + int number = 0; + int pos = 0; + while (pos < Length()) + { + pos = Find(oldChar, pos); + if (pos < 0) + break; + _chars[pos] = newChar; + pos++; + number++; + } + return number; + } + int Replace(const CStringBase &oldString, const CStringBase &newString) + { + if (oldString.IsEmpty()) + return 0; + if (oldString == newString) + return 0; + int oldStringLength = oldString.Length(); + int newStringLength = newString.Length(); + int number = 0; + int pos = 0; + while (pos < _length) + { + pos = Find(oldString, pos); + if (pos < 0) + break; + Delete(pos, oldStringLength); + Insert(pos, newString); + pos += newStringLength; + number++; + } + return number; + } + int Delete(int index, int count = 1 ) + { + if (index + count > _length) + count = _length - index; + if (count > 0) + { + MoveItems(index, index + count); + _length -= count; + } + return _length; + } +}; + +template +CStringBase operator+(const CStringBase& s1, const CStringBase& s2) +{ + CStringBase result(s1); + result += s2; + return result; +} + +template +CStringBase operator+(const CStringBase& s, T c) +{ + CStringBase result(s); + result += c; + return result; +} + +template +CStringBase operator+(T c, const CStringBase& s) +{ + CStringBase result(c); + result += s; + return result; +} + +template +CStringBase operator+(const CStringBase& s, const T * chars) +{ + CStringBase result(s); + result += chars; + return result; +} + +template +CStringBase operator+(const T * chars, const CStringBase& s) +{ + CStringBase result(chars); + result += s; + return result; +} + +template +bool operator==(const CStringBase& s1, const CStringBase& s2) + { return (s1.Compare(s2) == 0); } + +template +bool operator<(const CStringBase& s1, const CStringBase& s2) + { return (s1.Compare(s2) < 0); } + +template +bool operator==(const T *s1, const CStringBase& s2) + { return (s2.Compare(s1) == 0); } + +template +bool operator==(const CStringBase& s1, const T *s2) + { return (s1.Compare(s2) == 0); } + +template +bool operator!=(const CStringBase& s1, const CStringBase& s2) + { return (s1.Compare(s2) != 0); } + +template +bool operator!=(const T *s1, const CStringBase& s2) + { return (s2.Compare(s1) != 0); } + +template +bool operator!=(const CStringBase& s1, const T *s2) + { return (s1.Compare(s2) != 0); } + +typedef CStringBase AString; +typedef CStringBase UString; + +typedef CObjectVector AStringVector; +typedef CObjectVector UStringVector; + +#ifdef _UNICODE + typedef UString CSysString; +#else + typedef AString CSysString; +#endif + +typedef CObjectVector CSysStringVector; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyUnknown.h b/3rdparty/physfs/lzma/CPP/Common/MyUnknown.h new file mode 100644 index 0000000..d28d854 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyUnknown.h @@ -0,0 +1,24 @@ +// MyUnknown.h + +#ifndef __MYUNKNOWN_H +#define __MYUNKNOWN_H + +#ifdef _WIN32 + +#ifdef _WIN32_WCE +#if (_WIN32_WCE > 300) +#include +#else +#define MIDL_INTERFACE(x) struct +#endif +#else +#include +#endif + +#include + +#else +#include "MyWindows.h" +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyVector.cpp b/3rdparty/physfs/lzma/CPP/Common/MyVector.cpp new file mode 100644 index 0000000..def2a58 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyVector.cpp @@ -0,0 +1,78 @@ +// Common/MyVector.cpp + +#include "StdAfx.h" + +#include + +#include "MyVector.h" + +CBaseRecordVector::~CBaseRecordVector() { Free(); } + +void CBaseRecordVector::Free() +{ + delete []((unsigned char *)_items); + _capacity = 0; + _size = 0; + _items = 0; +} + +void CBaseRecordVector::Clear() { DeleteFrom(0); } +void CBaseRecordVector::DeleteBack() { Delete(_size - 1); } +void CBaseRecordVector::DeleteFrom(int index) { Delete(index, _size - index); } + +void CBaseRecordVector::ReserveOnePosition() +{ + if (_size != _capacity) + return; + int delta; + if (_capacity > 64) + delta = _capacity / 2; + else if (_capacity > 8) + delta = 8; + else + delta = 4; + Reserve(_capacity + delta); +} + +void CBaseRecordVector::Reserve(int newCapacity) +{ + if (newCapacity <= _capacity) + return; + if ((unsigned)newCapacity >= ((unsigned)1 << (sizeof(unsigned) * 8 - 1))) + throw 1052353; + size_t newSize = (size_t)(unsigned)newCapacity * _itemSize; + if (newSize / _itemSize != (size_t)(unsigned)newCapacity) + throw 1052354; + unsigned char *p = new unsigned char[newSize]; + if (p == 0) + throw 1052355; + int numRecordsToMove = _capacity; + memmove(p, _items, _itemSize * numRecordsToMove); + delete [](unsigned char *)_items; + _items = p; + _capacity = newCapacity; +} + +void CBaseRecordVector::MoveItems(int destIndex, int srcIndex) +{ + memmove(((unsigned char *)_items) + destIndex * _itemSize, + ((unsigned char *)_items) + srcIndex * _itemSize, + _itemSize * (_size - srcIndex)); +} + +void CBaseRecordVector::InsertOneItem(int index) +{ + ReserveOnePosition(); + MoveItems(index + 1, index); + _size++; +} + +void CBaseRecordVector::Delete(int index, int num) +{ + TestIndexAndCorrectNum(index, num); + if (num > 0) + { + MoveItems(index, index + num); + _size -= num; + } +} diff --git a/3rdparty/physfs/lzma/CPP/Common/MyVector.h b/3rdparty/physfs/lzma/CPP/Common/MyVector.h new file mode 100644 index 0000000..ce370a5 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyVector.h @@ -0,0 +1,254 @@ +// Common/Vector.h + +#ifndef __COMMON_VECTOR_H +#define __COMMON_VECTOR_H + +#include "Defs.h" + +class CBaseRecordVector +{ + void MoveItems(int destIndex, int srcIndex); +protected: + int _capacity; + int _size; + void *_items; + size_t _itemSize; + + void ReserveOnePosition(); + void InsertOneItem(int index); + void TestIndexAndCorrectNum(int index, int &num) const + { if (index + num > _size) num = _size - index; } +public: + CBaseRecordVector(size_t itemSize): + _capacity(0), _size(0), _items(0), _itemSize(itemSize) {} + virtual ~CBaseRecordVector(); + void Free(); + int Size() const { return _size; } + bool IsEmpty() const { return (_size == 0); } + void Reserve(int newCapacity); + virtual void Delete(int index, int num = 1); + void Clear(); + void DeleteFrom(int index); + void DeleteBack(); +}; + +template +class CRecordVector: public CBaseRecordVector +{ +public: + CRecordVector():CBaseRecordVector(sizeof(T)){}; + CRecordVector(const CRecordVector &v): + CBaseRecordVector(sizeof(T)) { *this = v;} + CRecordVector& operator=(const CRecordVector &v) + { + Clear(); + return (*this += v); + } + CRecordVector& operator+=(const CRecordVector &v) + { + int size = v.Size(); + Reserve(Size() + size); + for(int i = 0; i < size; i++) + Add(v[i]); + return *this; + } + int Add(T item) + { + ReserveOnePosition(); + ((T *)_items)[_size] = item; + return _size++; + } + void Insert(int index, T item) + { + InsertOneItem(index); + ((T *)_items)[index] = item; + } + // T* GetPointer() const { return (T*)_items; } + // operator const T *() const { return _items; }; + const T& operator[](int index) const { return ((T *)_items)[index]; } + T& operator[](int index) { return ((T *)_items)[index]; } + const T& Front() const { return operator[](0); } + T& Front() { return operator[](0); } + const T& Back() const { return operator[](_size - 1); } + T& Back() { return operator[](_size - 1); } + + void Swap(int i, int j) + { + T temp = operator[](i); + operator[](i) = operator[](j); + operator[](j) = temp; + } + + int FindInSorted(const T& item) const + { + int left = 0, right = Size(); + while (left != right) + { + int mid = (left + right) / 2; + const T& midValue = (*this)[mid]; + if (item == midValue) + return mid; + if (item < midValue) + right = mid; + else + left = mid + 1; + } + return -1; + } + + int AddToUniqueSorted(const T& item) + { + int left = 0, right = Size(); + while (left != right) + { + int mid = (left + right) / 2; + const T& midValue = (*this)[mid]; + if (item == midValue) + return mid; + if (item < midValue) + right = mid; + else + left = mid + 1; + } + Insert(right, item); + return right; + } + + static void SortRefDown(T* p, int k, int size, int (*compare)(const T*, const T*, void *), void *param) + { + T temp = p[k]; + for (;;) + { + int s = (k << 1); + if (s > size) + break; + if (s < size && compare(p + s + 1, p + s, param) > 0) + s++; + if (compare(&temp, p + s, param) >= 0) + break; + p[k] = p[s]; + k = s; + } + p[k] = temp; + } + + void Sort(int (*compare)(const T*, const T*, void *), void *param) + { + int size = _size; + if (size <= 1) + return; + T* p = (&Front()) - 1; + { + int i = size / 2; + do + SortRefDown(p, i, size, compare, param); + while(--i != 0); + } + do + { + T temp = p[size]; + p[size--] = p[1]; + p[1] = temp; + SortRefDown(p, 1, size, compare, param); + } + while (size > 1); + } +}; + +typedef CRecordVector CIntVector; +typedef CRecordVector CUIntVector; +typedef CRecordVector CBoolVector; +typedef CRecordVector CByteVector; +typedef CRecordVector CPointerVector; + +template +class CObjectVector: public CPointerVector +{ +public: + CObjectVector(){}; + ~CObjectVector() { Clear(); } + CObjectVector(const CObjectVector &objectVector) + { *this = objectVector; } + CObjectVector& operator=(const CObjectVector &objectVector) + { + Clear(); + return (*this += objectVector); + } + CObjectVector& operator+=(const CObjectVector &objectVector) + { + int size = objectVector.Size(); + Reserve(Size() + size); + for(int i = 0; i < size; i++) + Add(objectVector[i]); + return *this; + } + const T& operator[](int index) const { return *((T *)CPointerVector::operator[](index)); } + T& operator[](int index) { return *((T *)CPointerVector::operator[](index)); } + T& Front() { return operator[](0); } + const T& Front() const { return operator[](0); } + T& Back() { return operator[](_size - 1); } + const T& Back() const { return operator[](_size - 1); } + int Add(const T& item) + { return CPointerVector::Add(new T(item)); } + void Insert(int index, const T& item) + { CPointerVector::Insert(index, new T(item)); } + virtual void Delete(int index, int num = 1) + { + TestIndexAndCorrectNum(index, num); + for(int i = 0; i < num; i++) + delete (T *)(((void **)_items)[index + i]); + CPointerVector::Delete(index, num); + } + int Find(const T& item) const + { + for(int i = 0; i < Size(); i++) + if (item == (*this)[i]) + return i; + return -1; + } + int FindInSorted(const T& item) const + { + int left = 0, right = Size(); + while (left != right) + { + int mid = (left + right) / 2; + const T& midValue = (*this)[mid]; + if (item == midValue) + return mid; + if (item < midValue) + right = mid; + else + left = mid + 1; + } + return -1; + } + int AddToSorted(const T& item) + { + int left = 0, right = Size(); + while (left != right) + { + int mid = (left + right) / 2; + const T& midValue = (*this)[mid]; + if (item == midValue) + { + right = mid + 1; + break; + } + if (item < midValue) + right = mid; + else + left = mid + 1; + } + Insert(right, item); + return right; + } + + void Sort(int (*compare)(void *const *, void *const *, void *), void *param) + { CPointerVector::Sort(compare, param); } + + static int CompareObjectItems(void *const *a1, void *const *a2, void * /* param */) + { return MyCompare(*(*((const T **)a1)), *(*((const T **)a2))); } + void Sort() { CPointerVector::Sort(CompareObjectItems, 0); } +}; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/MyWindows.h b/3rdparty/physfs/lzma/CPP/Common/MyWindows.h new file mode 100644 index 0000000..e388fb0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/MyWindows.h @@ -0,0 +1,214 @@ +// MyWindows.h + +#ifndef __MYWINDOWS_H +#define __MYWINDOWS_H + +#ifdef _WIN32 + +#include + +#define CHAR_PATH_SEPARATOR '\\' +#define WCHAR_PATH_SEPARATOR L'\\' +#define STRING_PATH_SEPARATOR "\\" +#define WSTRING_PATH_SEPARATOR L"\\" + +#else + +#define CHAR_PATH_SEPARATOR '/' +#define WCHAR_PATH_SEPARATOR L'/' +#define STRING_PATH_SEPARATOR "/" +#define WSTRING_PATH_SEPARATOR L"/" + +#include // for wchar_t +#include + +#include "MyGuidDef.h" + +typedef char CHAR; +typedef unsigned char UCHAR; + +#undef BYTE +typedef unsigned char BYTE; + +typedef short SHORT; +typedef unsigned short USHORT; + +#undef WORD +typedef unsigned short WORD; +typedef short VARIANT_BOOL; + +typedef int INT; +typedef Int32 INT32; +typedef unsigned int UINT; +typedef UInt32 UINT32; +typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit +typedef UINT32 ULONG; + +#undef DWORD +typedef UINT32 DWORD; + +typedef Int64 LONGLONG; +typedef UInt64 ULONGLONG; + +typedef struct LARGE_INTEGER { LONGLONG QuadPart; }LARGE_INTEGER; +typedef struct _ULARGE_INTEGER { ULONGLONG QuadPart;} ULARGE_INTEGER; + +typedef const CHAR *LPCSTR; +typedef CHAR TCHAR; +typedef const TCHAR *LPCTSTR; +typedef wchar_t WCHAR; +typedef WCHAR OLECHAR; +typedef const WCHAR *LPCWSTR; +typedef OLECHAR *BSTR; +typedef const OLECHAR *LPCOLESTR; +typedef OLECHAR *LPOLESTR; + +typedef struct _FILETIME +{ + DWORD dwLowDateTime; + DWORD dwHighDateTime; +}FILETIME; + +#define HRESULT LONG +#define FAILED(Status) ((HRESULT)(Status)<0) +typedef ULONG PROPID; +typedef LONG SCODE; + +#define S_OK ((HRESULT)0x00000000L) +#define S_FALSE ((HRESULT)0x00000001L) +#define E_NOTIMPL ((HRESULT)0x80004001L) +#define E_NOINTERFACE ((HRESULT)0x80004002L) +#define E_ABORT ((HRESULT)0x80004004L) +#define E_FAIL ((HRESULT)0x80004005L) +#define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L) +#define E_OUTOFMEMORY ((HRESULT)0x8007000EL) +#define E_INVALIDARG ((HRESULT)0x80070057L) + +#ifdef _MSC_VER +#define STDMETHODCALLTYPE __stdcall +#else +#define STDMETHODCALLTYPE +#endif + +#define STDMETHOD_(t, f) virtual t STDMETHODCALLTYPE f +#define STDMETHOD(f) STDMETHOD_(HRESULT, f) +#define STDMETHODIMP_(type) type STDMETHODCALLTYPE +#define STDMETHODIMP STDMETHODIMP_(HRESULT) + +#define PURE = 0 + +#define MIDL_INTERFACE(x) struct + +#ifdef __cplusplus + +DEFINE_GUID(IID_IUnknown, +0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); +struct IUnknown +{ + STDMETHOD(QueryInterface) (REFIID iid, void **outObject) PURE; + STDMETHOD_(ULONG, AddRef)() PURE; + STDMETHOD_(ULONG, Release)() PURE; + #ifndef _WIN32 + virtual ~IUnknown() {} + #endif +}; + +typedef IUnknown *LPUNKNOWN; + +#endif + +#define VARIANT_TRUE ((VARIANT_BOOL)-1) +#define VARIANT_FALSE ((VARIANT_BOOL)0) + +enum VARENUM +{ + VT_EMPTY = 0, + VT_NULL = 1, + VT_I2 = 2, + VT_I4 = 3, + VT_R4 = 4, + VT_R8 = 5, + VT_CY = 6, + VT_DATE = 7, + VT_BSTR = 8, + VT_DISPATCH = 9, + VT_ERROR = 10, + VT_BOOL = 11, + VT_VARIANT = 12, + VT_UNKNOWN = 13, + VT_DECIMAL = 14, + VT_I1 = 16, + VT_UI1 = 17, + VT_UI2 = 18, + VT_UI4 = 19, + VT_I8 = 20, + VT_UI8 = 21, + VT_INT = 22, + VT_UINT = 23, + VT_VOID = 24, + VT_HRESULT = 25, + VT_FILETIME = 64 +}; + +typedef unsigned short VARTYPE; +typedef WORD PROPVAR_PAD1; +typedef WORD PROPVAR_PAD2; +typedef WORD PROPVAR_PAD3; + +#ifdef __cplusplus + +typedef struct tagPROPVARIANT +{ + VARTYPE vt; + PROPVAR_PAD1 wReserved1; + PROPVAR_PAD2 wReserved2; + PROPVAR_PAD3 wReserved3; + union + { + CHAR cVal; + UCHAR bVal; + SHORT iVal; + USHORT uiVal; + LONG lVal; + ULONG ulVal; + INT intVal; + UINT uintVal; + LARGE_INTEGER hVal; + ULARGE_INTEGER uhVal; + VARIANT_BOOL boolVal; + SCODE scode; + FILETIME filetime; + BSTR bstrVal; + }; +} PROPVARIANT; + +typedef PROPVARIANT tagVARIANT; +typedef tagVARIANT VARIANT; +typedef VARIANT VARIANTARG; + +MY_EXTERN_C HRESULT VariantClear(VARIANTARG *prop); +MY_EXTERN_C HRESULT VariantCopy(VARIANTARG *dest, VARIANTARG *src); + +#endif + +MY_EXTERN_C BSTR SysAllocStringByteLen(LPCSTR psz, UINT len); +MY_EXTERN_C BSTR SysAllocString(const OLECHAR *sz); +MY_EXTERN_C void SysFreeString(BSTR bstr); +MY_EXTERN_C UINT SysStringByteLen(BSTR bstr); +MY_EXTERN_C UINT SysStringLen(BSTR bstr); + +MY_EXTERN_C DWORD GetLastError(); +MY_EXTERN_C LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2); + +#define CP_ACP 0 +#define CP_OEMCP 1 + +typedef enum tagSTREAM_SEEK +{ + STREAM_SEEK_SET = 0, + STREAM_SEEK_CUR = 1, + STREAM_SEEK_END = 2 +} STREAM_SEEK; + +#endif +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/NewHandler.cpp b/3rdparty/physfs/lzma/CPP/Common/NewHandler.cpp new file mode 100644 index 0000000..094eb64 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/NewHandler.cpp @@ -0,0 +1,116 @@ +// NewHandler.cpp + +#include "StdAfx.h" + +#include + +#include "NewHandler.h" + +// #define DEBUG_MEMORY_LEAK + +#ifndef DEBUG_MEMORY_LEAK + +#ifdef _WIN32 +void * +#ifdef _MSC_VER +__cdecl +#endif +operator new(size_t size) +{ + // void *p = ::HeapAlloc(::GetProcessHeap(), 0, size); + void *p = ::malloc(size); + if (p == 0) + throw CNewException(); + return p; +} + +void +#ifdef _MSC_VER +__cdecl +#endif +operator delete(void *p) throw() +{ + /* + if (p == 0) + return; + ::HeapFree(::GetProcessHeap(), 0, p); + */ + ::free(p); +} +#endif + +#else + +#pragma init_seg(lib) +const int kDebugSize = 1000000; +static void *a[kDebugSize]; +static int index = 0; + +static int numAllocs = 0; +void * __cdecl operator new(size_t size) +{ + numAllocs++; + void *p = HeapAlloc(GetProcessHeap(), 0, size); + if (index == 40) + { + int t = 1; + } + if (index < kDebugSize) + { + a[index] = p; + index++; + } + if (p == 0) + throw CNewException(); + printf("Alloc %6d, size = %8d\n", numAllocs, size); + return p; +} + +class CC +{ +public: + CC() + { + for (int i = 0; i < kDebugSize; i++) + a[i] = 0; + } + ~CC() + { + for (int i = 0; i < kDebugSize; i++) + if (a[i] != 0) + return; + } +} g_CC; + + +void __cdecl operator delete(void *p) +{ + if (p == 0) + return; + /* + for (int i = 0; i < index; i++) + if (a[i] == p) + a[i] = 0; + */ + HeapFree(GetProcessHeap(), 0, p); + numAllocs--; + printf("Free %d\n", numAllocs); +} + +#endif + +/* +int MemErrorVC(size_t) +{ + throw CNewException(); + // return 1; +} +CNewHandlerSetter::CNewHandlerSetter() +{ + // MemErrorOldVCFunction = _set_new_handler(MemErrorVC); +} +CNewHandlerSetter::~CNewHandlerSetter() +{ + // _set_new_handler(MemErrorOldVCFunction); +} +*/ diff --git a/3rdparty/physfs/lzma/CPP/Common/NewHandler.h b/3rdparty/physfs/lzma/CPP/Common/NewHandler.h new file mode 100644 index 0000000..0619fc6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/NewHandler.h @@ -0,0 +1,16 @@ +// Common/NewHandler.h + +#ifndef __COMMON_NEWHANDLER_H +#define __COMMON_NEWHANDLER_H + +class CNewException {}; + +#ifdef _WIN32 +void +#ifdef _MSC_VER +__cdecl +#endif +operator delete(void *p) throw(); +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/StdAfx.h b/3rdparty/physfs/lzma/CPP/Common/StdAfx.h new file mode 100644 index 0000000..681ee93 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +// #include "MyWindows.h" +#include "NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/StdInStream.cpp b/3rdparty/physfs/lzma/CPP/Common/StdInStream.cpp new file mode 100644 index 0000000..8fed7bc --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StdInStream.cpp @@ -0,0 +1,84 @@ +// Common/StdInStream.cpp + +#include "StdAfx.h" + +#include +#include "StdInStream.h" + +#ifdef _MSC_VER +// "was declared deprecated" disabling +#pragma warning(disable : 4996 ) +#endif + +static const char kIllegalChar = '\0'; +static const char kNewLineChar = '\n'; + +static const char *kEOFMessage = "Unexpected end of input stream"; +static const char *kReadErrorMessage ="Error reading input stream"; +static const char *kIllegalCharMessage = "Illegal character in input stream"; + +static LPCTSTR kFileOpenMode = TEXT("r"); + +CStdInStream g_StdIn(stdin); + +bool CStdInStream::Open(LPCTSTR fileName) +{ + Close(); + _stream = _tfopen(fileName, kFileOpenMode); + _streamIsOpen = (_stream != 0); + return _streamIsOpen; +} + +bool CStdInStream::Close() +{ + if(!_streamIsOpen) + return true; + _streamIsOpen = (fclose(_stream) != 0); + return !_streamIsOpen; +} + +CStdInStream::~CStdInStream() +{ + Close(); +} + +AString CStdInStream::ScanStringUntilNewLine() +{ + AString s; + for (;;) + { + int intChar = GetChar(); + if(intChar == EOF) + throw kEOFMessage; + char c = char(intChar); + if (c == kIllegalChar) + throw kIllegalCharMessage; + if(c == kNewLineChar) + break; + s += c; + } + return s; +} + +void CStdInStream::ReadToString(AString &resultString) +{ + resultString.Empty(); + int c; + while((c = GetChar()) != EOF) + resultString += char(c); +} + +bool CStdInStream::Eof() +{ + return (feof(_stream) != 0); +} + +int CStdInStream::GetChar() +{ + int c = fgetc(_stream); // getc() doesn't work in BeOS? + if(c == EOF && !Eof()) + throw kReadErrorMessage; + return c; +} + + diff --git a/3rdparty/physfs/lzma/CPP/Common/StdInStream.h b/3rdparty/physfs/lzma/CPP/Common/StdInStream.h new file mode 100644 index 0000000..e0fb3df --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StdInStream.h @@ -0,0 +1,31 @@ +// Common/StdInStream.h + +#ifndef __COMMON_STDINSTREAM_H +#define __COMMON_STDINSTREAM_H + +#include + +#include "MyString.h" +#include "Types.h" + +class CStdInStream +{ + bool _streamIsOpen; + FILE *_stream; +public: + CStdInStream(): _streamIsOpen(false) {}; + CStdInStream(FILE *stream): _streamIsOpen(false), _stream(stream) {}; + ~CStdInStream(); + bool Open(LPCTSTR fileName); + bool Close(); + + AString ScanStringUntilNewLine(); + void ReadToString(AString &resultString); + + bool Eof(); + int GetChar(); +}; + +extern CStdInStream g_StdIn; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/StdOutStream.cpp b/3rdparty/physfs/lzma/CPP/Common/StdOutStream.cpp new file mode 100644 index 0000000..5498c0c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StdOutStream.cpp @@ -0,0 +1,93 @@ +// Common/StdOutStream.cpp + +#include "StdAfx.h" + +#include + +#include "StdOutStream.h" +#include "IntToString.h" +#include "StringConvert.h" + +#ifdef _MSC_VER +// "was declared deprecated" disabling +#pragma warning(disable : 4996 ) +#endif + +static const char kNewLineChar = '\n'; + +static const char *kFileOpenMode = "wt"; + +CStdOutStream g_StdOut(stdout); +CStdOutStream g_StdErr(stderr); + +bool CStdOutStream::Open(const char *fileName) +{ + Close(); + _stream = fopen(fileName, kFileOpenMode); + _streamIsOpen = (_stream != 0); + return _streamIsOpen; +} + +bool CStdOutStream::Close() +{ + if(!_streamIsOpen) + return true; + if (fclose(_stream) != 0) + return false; + _stream = 0; + _streamIsOpen = false; + return true; +} + +bool CStdOutStream::Flush() +{ + return (fflush(_stream) == 0); +} + +CStdOutStream::~CStdOutStream () +{ + Close(); +} + +CStdOutStream & CStdOutStream::operator<<(CStdOutStream & (*aFunction)(CStdOutStream &)) +{ + (*aFunction)(*this); + return *this; +} + +CStdOutStream & endl(CStdOutStream & outStream) +{ + return outStream << kNewLineChar; +} + +CStdOutStream & CStdOutStream::operator<<(const char *string) +{ + fputs(string, _stream); + return *this; +} + +CStdOutStream & CStdOutStream::operator<<(const wchar_t *string) +{ + *this << (const char *)UnicodeStringToMultiByte(string, CP_OEMCP); + return *this; +} + +CStdOutStream & CStdOutStream::operator<<(char c) +{ + fputc(c, _stream); + return *this; +} + +CStdOutStream & CStdOutStream::operator<<(int number) +{ + char textString[32]; + ConvertInt64ToString(number, textString); + return operator<<(textString); +} + +CStdOutStream & CStdOutStream::operator<<(UInt64 number) +{ + char textString[32]; + ConvertUInt64ToString(number, textString); + return operator<<(textString); +} diff --git a/3rdparty/physfs/lzma/CPP/Common/StdOutStream.h b/3rdparty/physfs/lzma/CPP/Common/StdOutStream.h new file mode 100644 index 0000000..8490736 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StdOutStream.h @@ -0,0 +1,35 @@ +// Common/StdOutStream.h + +#ifndef __COMMON_STDOUTSTREAM_H +#define __COMMON_STDOUTSTREAM_H + +#include + +#include "Types.h" + +class CStdOutStream +{ + bool _streamIsOpen; + FILE *_stream; +public: + CStdOutStream (): _streamIsOpen(false), _stream(0) {}; + CStdOutStream (FILE *stream): _streamIsOpen(false), _stream(stream) {}; + ~CStdOutStream (); + operator FILE *() { return _stream; } + bool Open(const char *fileName); + bool Close(); + bool Flush(); + CStdOutStream & operator<<(CStdOutStream & (* aFunction)(CStdOutStream &)); + CStdOutStream & operator<<(const char *string); + CStdOutStream & operator<<(const wchar_t *string); + CStdOutStream & operator<<(char c); + CStdOutStream & operator<<(int number); + CStdOutStream & operator<<(UInt64 number); +}; + +CStdOutStream & endl(CStdOutStream & outStream); + +extern CStdOutStream g_StdOut; +extern CStdOutStream g_StdErr; + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/StringConvert.cpp b/3rdparty/physfs/lzma/CPP/Common/StringConvert.cpp new file mode 100644 index 0000000..c0b19e1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StringConvert.cpp @@ -0,0 +1,94 @@ +// Common/StringConvert.cpp + +#include "StdAfx.h" + +#include "StringConvert.h" + +#ifndef _WIN32 +#include +#endif + +#ifdef _WIN32 +UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) +{ + UString resultString; + if(!srcString.IsEmpty()) + { + int numChars = MultiByteToWideChar(codePage, 0, srcString, + srcString.Length(), resultString.GetBuffer(srcString.Length()), + srcString.Length() + 1); + #ifndef _WIN32_WCE + if(numChars == 0) + throw 282228; + #endif + resultString.ReleaseBuffer(numChars); + } + return resultString; +} + +AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) +{ + AString resultString; + if(!srcString.IsEmpty()) + { + int numRequiredBytes = srcString.Length() * 2; + char defaultChar = '_'; + int numChars = WideCharToMultiByte(codePage, 0, srcString, + srcString.Length(), resultString.GetBuffer(numRequiredBytes), + numRequiredBytes + 1, &defaultChar, NULL); + #ifndef _WIN32_WCE + if(numChars == 0) + throw 282229; + #endif + resultString.ReleaseBuffer(numChars); + } + return resultString; +} + +#ifndef _WIN32_WCE +AString SystemStringToOemString(const CSysString &srcString) +{ + AString result; + CharToOem(srcString, result.GetBuffer(srcString.Length() * 2)); + result.ReleaseBuffer(); + return result; +} +#endif + +#else + +UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) +{ + UString resultString; + for (int i = 0; i < srcString.Length(); i++) + resultString += wchar_t(srcString[i]); + /* + if(!srcString.IsEmpty()) + { + int numChars = mbstowcs(resultString.GetBuffer(srcString.Length()), srcString, srcString.Length() + 1); + if (numChars < 0) throw "Your environment does not support UNICODE"; + resultString.ReleaseBuffer(numChars); + } + */ + return resultString; +} + +AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) +{ + AString resultString; + for (int i = 0; i < srcString.Length(); i++) + resultString += char(srcString[i]); + /* + if(!srcString.IsEmpty()) + { + int numRequiredBytes = srcString.Length() * 6 + 1; + int numChars = wcstombs(resultString.GetBuffer(numRequiredBytes), srcString, numRequiredBytes); + if (numChars < 0) throw "Your environment does not support UNICODE"; + resultString.ReleaseBuffer(numChars); + } + */ + return resultString; +} + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/Common/StringConvert.h b/3rdparty/physfs/lzma/CPP/Common/StringConvert.h new file mode 100644 index 0000000..32d8a3a --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StringConvert.h @@ -0,0 +1,71 @@ +// Common/StringConvert.h + +#ifndef __COMMON_STRINGCONVERT_H +#define __COMMON_STRINGCONVERT_H + +#include "MyWindows.h" +#include "MyString.h" +#include "Types.h" + +UString MultiByteToUnicodeString(const AString &srcString, UINT codePage = CP_ACP); +AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage = CP_ACP); + +inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString) + { return unicodeString; } +inline const UString& GetUnicodeString(const UString &unicodeString) + { return unicodeString; } +inline UString GetUnicodeString(const AString &ansiString) + { return MultiByteToUnicodeString(ansiString); } +inline UString GetUnicodeString(const AString &multiByteString, UINT codePage) + { return MultiByteToUnicodeString(multiByteString, codePage); } +inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString, UINT) + { return unicodeString; } +inline const UString& GetUnicodeString(const UString &unicodeString, UINT) + { return unicodeString; } + +inline const char* GetAnsiString(const char* ansiString) + { return ansiString; } +inline const AString& GetAnsiString(const AString &ansiString) + { return ansiString; } +inline AString GetAnsiString(const UString &unicodeString) + { return UnicodeStringToMultiByte(unicodeString); } + +inline const char* GetOemString(const char* oemString) + { return oemString; } +inline const AString& GetOemString(const AString &oemString) + { return oemString; } +inline AString GetOemString(const UString &unicodeString) + { return UnicodeStringToMultiByte(unicodeString, CP_OEMCP); } + + +#ifdef _UNICODE + inline const wchar_t* GetSystemString(const wchar_t* unicodeString) + { return unicodeString;} + inline const UString& GetSystemString(const UString &unicodeString) + { return unicodeString;} + inline const wchar_t* GetSystemString(const wchar_t* unicodeString, UINT /* codePage */) + { return unicodeString;} + inline const UString& GetSystemString(const UString &unicodeString, UINT /* codePage */) + { return unicodeString;} + inline UString GetSystemString(const AString &multiByteString, UINT codePage) + { return MultiByteToUnicodeString(multiByteString, codePage);} + inline UString GetSystemString(const AString &multiByteString) + { return MultiByteToUnicodeString(multiByteString);} +#else + inline const char* GetSystemString(const char *ansiString) + { return ansiString; } + inline const AString& GetSystemString(const AString &multiByteString, UINT) + { return multiByteString; } + inline const char * GetSystemString(const char *multiByteString, UINT) + { return multiByteString; } + inline AString GetSystemString(const UString &unicodeString) + { return UnicodeStringToMultiByte(unicodeString); } + inline AString GetSystemString(const UString &unicodeString, UINT codePage) + { return UnicodeStringToMultiByte(unicodeString, codePage); } +#endif + +#ifndef _WIN32_WCE +AString SystemStringToOemString(const CSysString &srcString); +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/StringToInt.cpp b/3rdparty/physfs/lzma/CPP/Common/StringToInt.cpp new file mode 100644 index 0000000..ec6733e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StringToInt.cpp @@ -0,0 +1,68 @@ +// Common/StringToInt.cpp + +#include "StdAfx.h" + +#include "StringToInt.h" + +UInt64 ConvertStringToUInt64(const char *s, const char **end) +{ + UInt64 result = 0; + for (;;) + { + char c = *s; + if (c < '0' || c > '9') + { + if (end != NULL) + *end = s; + return result; + } + result *= 10; + result += (c - '0'); + s++; + } +} + +UInt64 ConvertOctStringToUInt64(const char *s, const char **end) +{ + UInt64 result = 0; + for (;;) + { + char c = *s; + if (c < '0' || c > '7') + { + if (end != NULL) + *end = s; + return result; + } + result <<= 3; + result += (c - '0'); + s++; + } +} + + +UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end) +{ + UInt64 result = 0; + for (;;) + { + wchar_t c = *s; + if (c < '0' || c > '9') + { + if (end != NULL) + *end = s; + return result; + } + result *= 10; + result += (c - '0'); + s++; + } +} + + +Int64 ConvertStringToInt64(const char *s, const char **end) +{ + if (*s == '-') + return -(Int64)ConvertStringToUInt64(s + 1, end); + return ConvertStringToUInt64(s, end); +} diff --git a/3rdparty/physfs/lzma/CPP/Common/StringToInt.h b/3rdparty/physfs/lzma/CPP/Common/StringToInt.h new file mode 100644 index 0000000..bb971f6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/StringToInt.h @@ -0,0 +1,17 @@ +// Common/StringToInt.h + +#ifndef __COMMON_STRINGTOINT_H +#define __COMMON_STRINGTOINT_H + +#include +#include "Types.h" + +UInt64 ConvertStringToUInt64(const char *s, const char **end); +UInt64 ConvertOctStringToUInt64(const char *s, const char **end); +UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end); + +Int64 ConvertStringToInt64(const char *s, const char **end); + +#endif + + diff --git a/3rdparty/physfs/lzma/CPP/Common/Types.h b/3rdparty/physfs/lzma/CPP/Common/Types.h new file mode 100644 index 0000000..41d785e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/Types.h @@ -0,0 +1,57 @@ +// Common/Types.h + +#ifndef __COMMON_TYPES_H +#define __COMMON_TYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_INT16_DEFINED +#define _7ZIP_INT16_DEFINED +typedef short Int16; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_INT32_DEFINED +#define _7ZIP_INT32_DEFINED +typedef int Int32; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +typedef unsigned int UInt32; +#endif + +#ifdef _MSC_VER + +#ifndef _7ZIP_INT64_DEFINED +#define _7ZIP_INT64_DEFINED +typedef __int64 Int64; +#endif + +#ifndef _7ZIP_UINT64_DEFINED +#define _7ZIP_UINT64_DEFINED +typedef unsigned __int64 UInt64; +#endif + +#else + +#ifndef _7ZIP_INT64_DEFINED +#define _7ZIP_INT64_DEFINED +typedef long long int Int64; +#endif + +#ifndef _7ZIP_UINT64_DEFINED +#define _7ZIP_UINT64_DEFINED +typedef unsigned long long int UInt64; +#endif + +#endif + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/UTFConvert.cpp b/3rdparty/physfs/lzma/CPP/Common/UTFConvert.cpp new file mode 100644 index 0000000..e15695b --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/UTFConvert.cpp @@ -0,0 +1,91 @@ +// UTFConvert.cpp + +#include "StdAfx.h" + +#include "UTFConvert.h" +#include "Types.h" + +static Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +// These functions are for UTF8 <-> UTF16 conversion. + +bool ConvertUTF8ToUnicode(const AString &src, UString &dest) +{ + dest.Empty(); + for(int i = 0; i < src.Length();) + { + Byte c = (Byte)src[i++]; + if (c < 0x80) + { + dest += (wchar_t)c; + continue; + } + if(c < 0xC0) + return false; + int numAdds; + for (numAdds = 1; numAdds < 5; numAdds++) + if (c < kUtf8Limits[numAdds]) + break; + UInt32 value = (c - kUtf8Limits[numAdds - 1]); + do + { + if (i >= src.Length()) + return false; + Byte c2 = (Byte)src[i++]; + if (c2 < 0x80 || c2 >= 0xC0) + return false; + value <<= 6; + value |= (c2 - 0x80); + numAdds--; + } + while(numAdds > 0); + if (value < 0x10000) + dest += (wchar_t)(value); + else + { + value -= 0x10000; + if (value >= 0x100000) + return false; + dest += (wchar_t)(0xD800 + (value >> 10)); + dest += (wchar_t)(0xDC00 + (value & 0x3FF)); + } + } + return true; +} + +bool ConvertUnicodeToUTF8(const UString &src, AString &dest) +{ + dest.Empty(); + for(int i = 0; i < src.Length();) + { + UInt32 value = (UInt32)src[i++]; + if (value < 0x80) + { + dest += (char)value; + continue; + } + if (value >= 0xD800 && value < 0xE000) + { + if (value >= 0xDC00) + return false; + if (i >= src.Length()) + return false; + UInt32 c2 = (UInt32)src[i++]; + if (c2 < 0xDC00 || c2 >= 0xE000) + return false; + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + int numAdds; + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + dest += (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); + do + { + numAdds--; + dest += (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); + } + while(numAdds > 0); + } + return true; +} diff --git a/3rdparty/physfs/lzma/CPP/Common/UTFConvert.h b/3rdparty/physfs/lzma/CPP/Common/UTFConvert.h new file mode 100644 index 0000000..2a14600 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/UTFConvert.h @@ -0,0 +1,11 @@ +// Common/UTFConvert.h + +#ifndef __COMMON_UTFCONVERT_H +#define __COMMON_UTFCONVERT_H + +#include "MyString.h" + +bool ConvertUTF8ToUnicode(const AString &utfString, UString &resultString); +bool ConvertUnicodeToUTF8(const UString &unicodeString, AString &resultString); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Common/Wildcard.cpp b/3rdparty/physfs/lzma/CPP/Common/Wildcard.cpp new file mode 100644 index 0000000..9feebbe --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/Wildcard.cpp @@ -0,0 +1,458 @@ +// Common/Wildcard.cpp + +#include "StdAfx.h" + +#include "Wildcard.h" + +bool g_CaseSensitive = + #ifdef _WIN32 + false; + #else + true; + #endif + +static const wchar_t kAnyCharsChar = L'*'; +static const wchar_t kAnyCharChar = L'?'; + +#ifdef _WIN32 +static const wchar_t kDirDelimiter1 = L'\\'; +#endif +static const wchar_t kDirDelimiter2 = L'/'; + +static const UString kWildCardCharSet = L"?*"; + +static const UString kIllegalWildCardFileNameChars= + L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF" + L"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + L"\"/:<>\\|"; + + +static inline bool IsCharDirLimiter(wchar_t c) +{ + return ( + #ifdef _WIN32 + c == kDirDelimiter1 || + #endif + c == kDirDelimiter2); +} + +int CompareFileNames(const UString &s1, const UString &s2) +{ + if (g_CaseSensitive) + return s1.Compare(s2); + return s1.CompareNoCase(s2); +} + +// ----------------------------------------- +// this function compares name with mask +// ? - any char +// * - any char or empty + +static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name) +{ + for (;;) + { + wchar_t m = *mask; + wchar_t c = *name; + if (m == 0) + return (c == 0); + if (m == kAnyCharsChar) + { + if (EnhancedMaskTest(mask + 1, name)) + return true; + if (c == 0) + return false; + } + else + { + if (m == kAnyCharChar) + { + if (c == 0) + return false; + } + else if (m != c) + if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c)) + return false; + mask++; + } + name++; + } +} + +// -------------------------------------------------- +// Splits path to strings + +void SplitPathToParts(const UString &path, UStringVector &pathParts) +{ + pathParts.Clear(); + UString name; + int len = path.Length(); + if (len == 0) + return; + for (int i = 0; i < len; i++) + { + wchar_t c = path[i]; + if (IsCharDirLimiter(c)) + { + pathParts.Add(name); + name.Empty(); + } + else + name += c; + } + pathParts.Add(name); +} + +void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name) +{ + int i; + for(i = path.Length() - 1; i >= 0; i--) + if(IsCharDirLimiter(path[i])) + break; + dirPrefix = path.Left(i + 1); + name = path.Mid(i + 1); +} + +UString ExtractDirPrefixFromPath(const UString &path) +{ + int i; + for(i = path.Length() - 1; i >= 0; i--) + if(IsCharDirLimiter(path[i])) + break; + return path.Left(i + 1); +} + +UString ExtractFileNameFromPath(const UString &path) +{ + int i; + for(i = path.Length() - 1; i >= 0; i--) + if(IsCharDirLimiter(path[i])) + break; + return path.Mid(i + 1); +} + + +bool CompareWildCardWithName(const UString &mask, const UString &name) +{ + return EnhancedMaskTest(mask, name); +} + +bool DoesNameContainWildCard(const UString &path) +{ + return (path.FindOneOf(kWildCardCharSet) >= 0); +} + + +// ----------------------------------------------------------' +// NWildcard + +namespace NWildcard { + + +/* +M = MaskParts.Size(); +N = TestNameParts.Size(); + + File Dir +ForFile req M<=N [N-M, N) - + nonreq M=N [0, M) - + +ForDir req M 1) + return true; + } + return false; +} + +bool CCensorNode::AreThereIncludeItems() const +{ + if (IncludeItems.Size() > 0) + return true; + for (int i = 0; i < SubNodes.Size(); i++) + if (SubNodes[i].AreThereIncludeItems()) + return true; + return false; +} + +bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const +{ + const CObjectVector &items = include ? IncludeItems : ExcludeItems; + for (int i = 0; i < items.Size(); i++) + if (items[i].CheckPath(pathParts, isFile)) + return true; + return false; +} + +bool CCensorNode::CheckPath(UStringVector &pathParts, bool isFile, bool &include) const +{ + if (CheckPathCurrent(false, pathParts, isFile)) + { + include = false; + return true; + } + include = true; + bool finded = CheckPathCurrent(true, pathParts, isFile); + if (pathParts.Size() == 1) + return finded; + int index = FindSubNode(pathParts.Front()); + if (index >= 0) + { + UStringVector pathParts2 = pathParts; + pathParts2.Delete(0); + if (SubNodes[index].CheckPath(pathParts2, isFile, include)) + return true; + } + return finded; +} + +bool CCensorNode::CheckPath(const UString &path, bool isFile, bool &include) const +{ + UStringVector pathParts; + SplitPathToParts(path, pathParts); + return CheckPath(pathParts, isFile, include); +} + +bool CCensorNode::CheckPath(const UString &path, bool isFile) const +{ + bool include; + if(CheckPath(path, isFile, include)) + return include; + return false; +} + +bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const +{ + if (CheckPathCurrent(include, pathParts, isFile)) + return true; + if (Parent == 0) + return false; + pathParts.Insert(0, Name); + return Parent->CheckPathToRoot(include, pathParts, isFile); +} + +/* +bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const +{ + UStringVector pathParts; + SplitPathToParts(path, pathParts); + return CheckPathToRoot(include, pathParts, isFile); +} +*/ + +void CCensorNode::AddItem2(bool include, const UString &path, bool recursive) +{ + if (path.IsEmpty()) + return; + bool forFile = true; + bool forFolder = true; + UString path2 = path; + if (IsCharDirLimiter(path[path.Length() - 1])) + { + path2.Delete(path.Length() - 1); + forFile = false; + } + AddItem(include, path2, recursive, forFile, forFolder); +} + +void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) +{ + ExcludeItems += fromNodes.ExcludeItems; + for (int i = 0; i < fromNodes.SubNodes.Size(); i++) + { + const CCensorNode &node = fromNodes.SubNodes[i]; + int subNodeIndex = FindSubNode(node.Name); + if (subNodeIndex < 0) + subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this)); + SubNodes[subNodeIndex].ExtendExclude(node); + } +} + +int CCensor::FindPrefix(const UString &prefix) const +{ + for (int i = 0; i < Pairs.Size(); i++) + if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) + return i; + return -1; +} + +void CCensor::AddItem(bool include, const UString &path, bool recursive) +{ + UStringVector pathParts; + SplitPathToParts(path, pathParts); + bool forFile = true; + if (pathParts.Back().IsEmpty()) + { + forFile = false; + pathParts.DeleteBack(); + } + const UString &front = pathParts.Front(); + bool isAbs = false; + if (front.IsEmpty()) + isAbs = true; + else if (front.Length() == 2 && front[1] == L':') + isAbs = true; + else + { + for (int i = 0; i < pathParts.Size(); i++) + { + const UString &part = pathParts[i]; + if (part == L".." || part == L".") + { + isAbs = true; + break; + } + } + } + int numAbsParts = 0; + if (isAbs) + if (pathParts.Size() > 1) + numAbsParts = pathParts.Size() - 1; + else + numAbsParts = 1; + UString prefix; + for (int i = 0; i < numAbsParts; i++) + { + const UString &front = pathParts.Front(); + if (DoesNameContainWildCard(front)) + break; + prefix += front; + prefix += WCHAR_PATH_SEPARATOR; + pathParts.Delete(0); + } + int index = FindPrefix(prefix); + if (index < 0) + index = Pairs.Add(CPair(prefix)); + + CItem item; + item.PathParts = pathParts; + item.ForDir = true; + item.ForFile = forFile; + item.Recursive = recursive; + Pairs[index].Head.AddItem(include, item); +} + +bool CCensor::CheckPath(const UString &path, bool isFile) const +{ + bool finded = false; + for (int i = 0; i < Pairs.Size(); i++) + { + bool include; + if (Pairs[i].Head.CheckPath(path, isFile, include)) + { + if (!include) + return false; + finded = true; + } + } + return finded; +} + +void CCensor::ExtendExclude() +{ + int i; + for (i = 0; i < Pairs.Size(); i++) + if (Pairs[i].Prefix.IsEmpty()) + break; + if (i == Pairs.Size()) + return; + int index = i; + for (i = 0; i < Pairs.Size(); i++) + if (index != i) + Pairs[i].Head.ExtendExclude(Pairs[index].Head); +} + +} diff --git a/3rdparty/physfs/lzma/CPP/Common/Wildcard.h b/3rdparty/physfs/lzma/CPP/Common/Wildcard.h new file mode 100644 index 0000000..6d4cbce --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Common/Wildcard.h @@ -0,0 +1,80 @@ +// Common/Wildcard.h + +#ifndef __COMMON_WILDCARD_H +#define __COMMON_WILDCARD_H + +#include "MyString.h" + +int CompareFileNames(const UString &s1, const UString &s2); + +void SplitPathToParts(const UString &path, UStringVector &pathParts); +void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name); +UString ExtractDirPrefixFromPath(const UString &path); +UString ExtractFileNameFromPath(const UString &path); +bool DoesNameContainWildCard(const UString &path); +bool CompareWildCardWithName(const UString &mask, const UString &name); + +namespace NWildcard { + +struct CItem +{ + UStringVector PathParts; + bool Recursive; + bool ForFile; + bool ForDir; + bool CheckPath(const UStringVector &pathParts, bool isFile) const; +}; + +class CCensorNode +{ + CCensorNode *Parent; + bool CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const; + void AddItemSimple(bool include, CItem &item); + bool CheckPath(UStringVector &pathParts, bool isFile, bool &include) const; +public: + CCensorNode(): Parent(0) { }; + CCensorNode(const UString &name, CCensorNode *parent): Name(name), Parent(parent) { }; + UString Name; + CObjectVector SubNodes; + CObjectVector IncludeItems; + CObjectVector ExcludeItems; + + int FindSubNode(const UString &path) const; + + void AddItem(bool include, CItem &item); + void AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir); + void AddItem2(bool include, const UString &path, bool recursive); + + bool NeedCheckSubDirs() const; + bool AreThereIncludeItems() const; + + bool CheckPath(const UString &path, bool isFile, bool &include) const; + bool CheckPath(const UString &path, bool isFile) const; + + bool CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const; + // bool CheckPathToRoot(const UString &path, bool isFile, bool include) const; + void ExtendExclude(const CCensorNode &fromNodes); +}; + +struct CPair +{ + UString Prefix; + CCensorNode Head; + CPair(const UString &prefix): Prefix(prefix) { }; +}; + +class CCensor +{ + int FindPrefix(const UString &prefix) const; +public: + CObjectVector Pairs; + bool AllAreRelative() const + { return (Pairs.Size() == 1 && Pairs.Front().Prefix.IsEmpty()); } + void AddItem(bool include, const UString &path, bool recursive); + bool CheckPath(const UString &path, bool isFile) const; + void ExtendExclude(); +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/DLL.cpp b/3rdparty/physfs/lzma/CPP/Windows/DLL.cpp new file mode 100644 index 0000000..9e92dc2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/DLL.cpp @@ -0,0 +1,115 @@ +// Windows/DLL.cpp + +#include "StdAfx.h" + +#include "DLL.h" +#include "Defs.h" +#ifndef _UNICODE +#include "../Common/StringConvert.h" +#endif + +#ifndef _UNICODE +extern bool g_IsNT; +#endif + +namespace NWindows { +namespace NDLL { + +CLibrary::~CLibrary() +{ + Free(); +} + +bool CLibrary::Free() +{ + if (_module == 0) + return true; + // MessageBox(0, TEXT(""), TEXT("Free"), 0); + // Sleep(5000); + if (!::FreeLibrary(_module)) + return false; + _module = 0; + return true; +} + +bool CLibrary::LoadOperations(HMODULE newModule) +{ + if (newModule == NULL) + return false; + if(!Free()) + return false; + _module = newModule; + return true; +} + +bool CLibrary::LoadEx(LPCTSTR fileName, DWORD flags) +{ + // MessageBox(0, fileName, TEXT("LoadEx"), 0); + return LoadOperations(::LoadLibraryEx(fileName, NULL, flags)); +} + +bool CLibrary::Load(LPCTSTR fileName) +{ + // MessageBox(0, fileName, TEXT("Load"), 0); + // Sleep(5000); + // OutputDebugString(fileName); + // OutputDebugString(TEXT("\n")); + return LoadOperations(::LoadLibrary(fileName)); +} + +#ifndef _UNICODE +static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } +CSysString GetSysPath(LPCWSTR sysPath) + { return UnicodeStringToMultiByte(sysPath, GetCurrentCodePage()); } + +bool CLibrary::LoadEx(LPCWSTR fileName, DWORD flags) +{ + if (g_IsNT) + return LoadOperations(::LoadLibraryExW(fileName, NULL, flags)); + return LoadEx(GetSysPath(fileName), flags); +} +bool CLibrary::Load(LPCWSTR fileName) +{ + if (g_IsNT) + return LoadOperations(::LoadLibraryW(fileName)); + return Load(GetSysPath(fileName)); +} +#endif + +bool MyGetModuleFileName(HMODULE hModule, CSysString &result) +{ + result.Empty(); + TCHAR fullPath[MAX_PATH + 2]; + DWORD size = ::GetModuleFileName(hModule, fullPath, MAX_PATH + 1); + if (size <= MAX_PATH && size != 0) + { + result = fullPath; + return true; + } + return false; +} + +#ifndef _UNICODE +bool MyGetModuleFileName(HMODULE hModule, UString &result) +{ + result.Empty(); + if (g_IsNT) + { + wchar_t fullPath[MAX_PATH + 2]; + DWORD size = ::GetModuleFileNameW(hModule, fullPath, MAX_PATH + 1); + if (size <= MAX_PATH && size != 0) + { + result = fullPath; + return true; + } + return false; + } + CSysString resultSys; + if (!MyGetModuleFileName(hModule, resultSys)) + return false; + result = MultiByteToUnicodeString(resultSys, GetCurrentCodePage()); + return true; +} +#endif + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/DLL.h b/3rdparty/physfs/lzma/CPP/Windows/DLL.h new file mode 100644 index 0000000..4c2ffa2 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/DLL.h @@ -0,0 +1,54 @@ +// Windows/DLL.h + +#ifndef __WINDOWS_DLL_H +#define __WINDOWS_DLL_H + +#include "../Common/MyString.h" + +namespace NWindows { +namespace NDLL { + +class CLibrary +{ + bool LoadOperations(HMODULE newModule); +protected: + HMODULE _module; +public: + operator HMODULE() const { return _module; } + HMODULE* operator&() { return &_module; } + + CLibrary():_module(NULL) {}; + ~CLibrary(); + void Attach(HMODULE m) + { + Free(); + _module = m; + } + HMODULE Detach() + { + HMODULE m = _module; + _module = NULL; + return m; + } + + // operator HMODULE() const { return _module; }; + bool IsLoaded() const { return (_module != NULL); }; + bool Free(); + bool LoadEx(LPCTSTR fileName, DWORD flags = LOAD_LIBRARY_AS_DATAFILE); + bool Load(LPCTSTR fileName); + #ifndef _UNICODE + bool LoadEx(LPCWSTR fileName, DWORD flags = LOAD_LIBRARY_AS_DATAFILE); + bool Load(LPCWSTR fileName); + #endif + FARPROC GetProcAddress(LPCSTR procName) const + { return ::GetProcAddress(_module, procName); } +}; + +bool MyGetModuleFileName(HMODULE hModule, CSysString &result); +#ifndef _UNICODE +bool MyGetModuleFileName(HMODULE hModule, UString &result); +#endif + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Defs.h b/3rdparty/physfs/lzma/CPP/Windows/Defs.h new file mode 100644 index 0000000..898be8d --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Defs.h @@ -0,0 +1,23 @@ +// Windows/Defs.h + +#ifndef __WINDOWS_DEFS_H +#define __WINDOWS_DEFS_H + +inline bool BOOLToBool(BOOL value) + { return (value != FALSE); } + +#ifdef _WIN32 +inline bool LRESULTToBool(LRESULT value) + { return (value != FALSE); } +#endif + +inline BOOL BoolToBOOL(bool value) + { return (value ? TRUE: FALSE); } + +inline VARIANT_BOOL BoolToVARIANT_BOOL(bool value) + { return (value ? VARIANT_TRUE: VARIANT_FALSE); } + +inline bool VARIANT_BOOLToBool(VARIANT_BOOL value) + { return (value != VARIANT_FALSE); } + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Error.cpp b/3rdparty/physfs/lzma/CPP/Windows/Error.cpp new file mode 100644 index 0000000..e559c4c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Error.cpp @@ -0,0 +1,50 @@ +// Windows/Error.h + +#include "StdAfx.h" + +#include "Windows/Error.h" +#ifndef _UNICODE +#include "Common/StringConvert.h" +#endif + +#ifndef _UNICODE +extern bool g_IsNT; +#endif + +namespace NWindows { +namespace NError { + +bool MyFormatMessage(DWORD messageID, CSysString &message) +{ + LPVOID msgBuf; + if(::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL,messageID, 0, (LPTSTR) &msgBuf,0, NULL) == 0) + return false; + message = (LPCTSTR)msgBuf; + ::LocalFree(msgBuf); + return true; +} + +#ifndef _UNICODE +bool MyFormatMessage(DWORD messageID, UString &message) +{ + if (g_IsNT) + { + LPVOID msgBuf; + if(::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, messageID, 0, (LPWSTR) &msgBuf, 0, NULL) == 0) + return false; + message = (LPCWSTR)msgBuf; + ::LocalFree(msgBuf); + return true; + } + CSysString messageSys; + bool result = MyFormatMessage(messageID, messageSys); + message = GetUnicodeString(messageSys); + return result; +} +#endif + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/Error.h b/3rdparty/physfs/lzma/CPP/Windows/Error.h new file mode 100644 index 0000000..05b5cd0 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Error.h @@ -0,0 +1,33 @@ +// Windows/Error.h + +#ifndef __WINDOWS_ERROR_H +#define __WINDOWS_ERROR_H + +#include "Common/MyString.h" + +namespace NWindows { +namespace NError { + +bool MyFormatMessage(DWORD messageID, CSysString &message); +inline CSysString MyFormatMessage(DWORD messageID) +{ + CSysString message; + MyFormatMessage(messageID, message); + return message; +} +#ifdef _UNICODE +inline UString MyFormatMessageW(DWORD messageID) + { return MyFormatMessage(messageID); } +#else +bool MyFormatMessage(DWORD messageID, UString &message); +inline UString MyFormatMessageW(DWORD messageID) +{ + UString message; + MyFormatMessage(messageID, message); + return message; +} +#endif + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileDir.cpp b/3rdparty/physfs/lzma/CPP/Windows/FileDir.cpp new file mode 100644 index 0000000..6d56170 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileDir.cpp @@ -0,0 +1,841 @@ +// Windows/FileDir.cpp + +#include "StdAfx.h" + +#include "FileDir.h" +#include "FileName.h" +#include "FileFind.h" +#include "Defs.h" +#ifndef _UNICODE +#include "../Common/StringConvert.h" +#endif + +#ifndef _UNICODE +extern bool g_IsNT; +#endif + +namespace NWindows { +namespace NFile { + +#if defined(WIN_LONG_PATH) && defined(_UNICODE) +#define WIN_LONG_PATH2 +#endif + +// SetCurrentDirectory doesn't support \\?\ prefix + +#ifdef WIN_LONG_PATH +bool GetLongPathBase(LPCWSTR fileName, UString &res); +bool GetLongPath(LPCWSTR fileName, UString &res); +#endif + +namespace NDirectory { + +#ifndef _UNICODE +static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } +static UString GetUnicodePath(const CSysString &sysPath) + { return MultiByteToUnicodeString(sysPath, GetCurrentCodePage()); } +static CSysString GetSysPath(LPCWSTR sysPath) + { return UnicodeStringToMultiByte(sysPath, GetCurrentCodePage()); } +#endif + +bool MyGetWindowsDirectory(CSysString &path) +{ + UINT needLength = ::GetWindowsDirectory(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); +} + +bool MyGetSystemDirectory(CSysString &path) +{ + UINT needLength = ::GetSystemDirectory(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); +} + +#ifndef _UNICODE +bool MyGetWindowsDirectory(UString &path) +{ + if (g_IsNT) + { + UINT needLength = ::GetWindowsDirectoryW(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); + } + CSysString sysPath; + if (!MyGetWindowsDirectory(sysPath)) + return false; + path = GetUnicodePath(sysPath); + return true; +} + +bool MyGetSystemDirectory(UString &path) +{ + if (g_IsNT) + { + UINT needLength = ::GetSystemDirectoryW(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); + } + CSysString sysPath; + if (!MyGetSystemDirectory(sysPath)) + return false; + path = GetUnicodePath(sysPath); + return true; +} +#endif + +bool SetDirTime(LPCWSTR fileName, const FILETIME *creationTime, const FILETIME *lastAccessTime, const FILETIME *lastWriteTime) +{ + #ifndef _UNICODE + if (!g_IsNT) + { + ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return false; + } + #endif + HANDLE hDir = ::CreateFileW(fileName, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + #ifdef WIN_LONG_PATH + if (hDir == INVALID_HANDLE_VALUE) + { + UString longPath; + if (GetLongPath(fileName, longPath)) + hDir = ::CreateFileW(longPath, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + #endif + + bool res = false; + if (hDir != INVALID_HANDLE_VALUE) + { + res = BOOLToBool(::SetFileTime(hDir, creationTime, lastAccessTime, lastWriteTime)); + ::CloseHandle(hDir); + } + return res; +} + +bool MySetFileAttributes(LPCTSTR fileName, DWORD fileAttributes) +{ + if (::SetFileAttributes(fileName, fileAttributes)) + return true; + #ifdef WIN_LONG_PATH2 + UString longPath; + if (GetLongPath(fileName, longPath)) + return BOOLToBool(::SetFileAttributesW(longPath, fileAttributes)); + #endif + return false; +} + +bool MyRemoveDirectory(LPCTSTR pathName) +{ + if (::RemoveDirectory(pathName)) + return true; + #ifdef WIN_LONG_PATH2 + UString longPath; + if (GetLongPath(pathName, longPath)) + return BOOLToBool(::RemoveDirectoryW(longPath)); + #endif + return false; +} + +#ifdef WIN_LONG_PATH +bool GetLongPaths(LPCWSTR s1, LPCWSTR s2, UString &d1, UString &d2) +{ + if (!GetLongPathBase(s1, d1) || !GetLongPathBase(s2, d2)) + return false; + if (d1.IsEmpty() && d2.IsEmpty()) return false; + if (d1.IsEmpty()) d1 = s1; + if (d2.IsEmpty()) d2 = s2; + return true; +} +#endif + +bool MyMoveFile(LPCTSTR existFileName, LPCTSTR newFileName) +{ + if (::MoveFile(existFileName, newFileName)) + return true; + #ifdef WIN_LONG_PATH2 + UString d1, d2; + if (GetLongPaths(existFileName, newFileName, d1, d2)) + return BOOLToBool(::MoveFileW(d1, d2)); + #endif + return false; +} + +#ifndef _UNICODE +bool MySetFileAttributes(LPCWSTR fileName, DWORD fileAttributes) +{ + if (!g_IsNT) + return MySetFileAttributes(GetSysPath(fileName), fileAttributes); + if (::SetFileAttributesW(fileName, fileAttributes)) + return true; + #ifdef WIN_LONG_PATH + UString longPath; + if (GetLongPath(fileName, longPath)) + return BOOLToBool(::SetFileAttributesW(longPath, fileAttributes)); + #endif + return false; +} + + +bool MyRemoveDirectory(LPCWSTR pathName) +{ + if (!g_IsNT) + return MyRemoveDirectory(GetSysPath(pathName)); + if (::RemoveDirectoryW(pathName)) + return true; + #ifdef WIN_LONG_PATH + UString longPath; + if (GetLongPath(pathName, longPath)) + return BOOLToBool(::RemoveDirectoryW(longPath)); + #endif + return false; +} + +bool MyMoveFile(LPCWSTR existFileName, LPCWSTR newFileName) +{ + if (!g_IsNT) + return MyMoveFile(GetSysPath(existFileName), GetSysPath(newFileName)); + if (::MoveFileW(existFileName, newFileName)) + return true; + #ifdef WIN_LONG_PATH + UString d1, d2; + if (GetLongPaths(existFileName, newFileName, d1, d2)) + return BOOLToBool(::MoveFileW(d1, d2)); + #endif + return false; +} +#endif + +bool MyCreateDirectory(LPCTSTR pathName) +{ + if (::CreateDirectory(pathName, NULL)) + return true; + #ifdef WIN_LONG_PATH2 + if (::GetLastError() != ERROR_ALREADY_EXISTS) + { + UString longPath; + if (GetLongPath(pathName, longPath)) + return BOOLToBool(::CreateDirectoryW(longPath, NULL)); + } + #endif + return false; +} + +#ifndef _UNICODE +bool MyCreateDirectory(LPCWSTR pathName) +{ + if (!g_IsNT) + return MyCreateDirectory(GetSysPath(pathName)); + if (::CreateDirectoryW(pathName, NULL)) + return true; + #ifdef WIN_LONG_PATH + if (::GetLastError() != ERROR_ALREADY_EXISTS) + { + UString longPath; + if (GetLongPath(pathName, longPath)) + return BOOLToBool(::CreateDirectoryW(longPath, NULL)); + } + #endif + return false; +} +#endif + +/* +bool CreateComplexDirectory(LPCTSTR pathName) +{ + NName::CParsedPath path; + path.ParsePath(pathName); + CSysString fullPath = path.Prefix; + DWORD errorCode = ERROR_SUCCESS; + for(int i = 0; i < path.PathParts.Size(); i++) + { + const CSysString &string = path.PathParts[i]; + if(string.IsEmpty()) + { + if(i != path.PathParts.Size() - 1) + return false; + return true; + } + fullPath += path.PathParts[i]; + if (!MyCreateDirectory(fullPath)) + { + DWORD errorCode = GetLastError(); + if(errorCode != ERROR_ALREADY_EXISTS) + return false; + } + fullPath += NName::kDirDelimiter; + } + return true; +} +*/ + +bool CreateComplexDirectory(LPCTSTR _aPathName) +{ + CSysString pathName = _aPathName; + int pos = pathName.ReverseFind(TEXT(CHAR_PATH_SEPARATOR)); + if (pos > 0 && pos == pathName.Length() - 1) + { + if (pathName.Length() == 3 && pathName[1] == ':') + return true; // Disk folder; + pathName.Delete(pos); + } + CSysString pathName2 = pathName; + pos = pathName.Length(); + for (;;) + { + if(MyCreateDirectory(pathName)) + break; + if (::GetLastError() == ERROR_ALREADY_EXISTS) + { + NFind::CFileInfo fileInfo; + if (!NFind::FindFile(pathName, fileInfo)) // For network folders + return true; + if (!fileInfo.IsDirectory()) + return false; + break; + } + pos = pathName.ReverseFind(TEXT(CHAR_PATH_SEPARATOR)); + if (pos < 0 || pos == 0) + return false; + if (pathName[pos - 1] == ':') + return false; + pathName = pathName.Left(pos); + } + pathName = pathName2; + while(pos < pathName.Length()) + { + pos = pathName.Find(TEXT(CHAR_PATH_SEPARATOR), pos + 1); + if (pos < 0) + pos = pathName.Length(); + if (!MyCreateDirectory(pathName.Left(pos))) + return false; + } + return true; +} + +#ifndef _UNICODE + +bool CreateComplexDirectory(LPCWSTR _aPathName) +{ + UString pathName = _aPathName; + int pos = pathName.ReverseFind(WCHAR_PATH_SEPARATOR); + if (pos > 0 && pos == pathName.Length() - 1) + { + if (pathName.Length() == 3 && pathName[1] == L':') + return true; // Disk folder; + pathName.Delete(pos); + } + UString pathName2 = pathName; + pos = pathName.Length(); + for (;;) + { + if(MyCreateDirectory(pathName)) + break; + if (::GetLastError() == ERROR_ALREADY_EXISTS) + { + NFind::CFileInfoW fileInfo; + if (!NFind::FindFile(pathName, fileInfo)) // For network folders + return true; + if (!fileInfo.IsDirectory()) + return false; + break; + } + pos = pathName.ReverseFind(WCHAR_PATH_SEPARATOR); + if (pos < 0 || pos == 0) + return false; + if (pathName[pos - 1] == L':') + return false; + pathName = pathName.Left(pos); + } + pathName = pathName2; + while(pos < pathName.Length()) + { + pos = pathName.Find(WCHAR_PATH_SEPARATOR, pos + 1); + if (pos < 0) + pos = pathName.Length(); + if (!MyCreateDirectory(pathName.Left(pos))) + return false; + } + return true; +} + +#endif + +bool DeleteFileAlways(LPCTSTR name) +{ + if (!MySetFileAttributes(name, 0)) + return false; + if (::DeleteFile(name)) + return true; + #ifdef WIN_LONG_PATH2 + UString longPath; + if (GetLongPath(name, longPath)) + return BOOLToBool(::DeleteFileW(longPath)); + #endif + return false; +} + +#ifndef _UNICODE +bool DeleteFileAlways(LPCWSTR name) +{ + if (!g_IsNT) + return DeleteFileAlways(GetSysPath(name)); + if (!MySetFileAttributes(name, 0)) + return false; + if (::DeleteFileW(name)) + return true; + #ifdef WIN_LONG_PATH + UString longPath; + if (GetLongPath(name, longPath)) + return BOOLToBool(::DeleteFileW(longPath)); + #endif + return false; +} +#endif + +static bool RemoveDirectorySubItems2(const CSysString pathPrefix, const NFind::CFileInfo &fileInfo) +{ + if(fileInfo.IsDirectory()) + return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name); + return DeleteFileAlways(pathPrefix + fileInfo.Name); +} + +bool RemoveDirectoryWithSubItems(const CSysString &path) +{ + NFind::CFileInfo fileInfo; + CSysString pathPrefix = path + NName::kDirDelimiter; + { + NFind::CEnumerator enumerator(pathPrefix + TCHAR(NName::kAnyStringWildcard)); + while(enumerator.Next(fileInfo)) + if (!RemoveDirectorySubItems2(pathPrefix, fileInfo)) + return false; + } + if (!MySetFileAttributes(path, 0)) + return false; + return MyRemoveDirectory(path); +} + +#ifndef _UNICODE +static bool RemoveDirectorySubItems2(const UString pathPrefix, const NFind::CFileInfoW &fileInfo) +{ + if(fileInfo.IsDirectory()) + return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name); + return DeleteFileAlways(pathPrefix + fileInfo.Name); +} +bool RemoveDirectoryWithSubItems(const UString &path) +{ + NFind::CFileInfoW fileInfo; + UString pathPrefix = path + UString(NName::kDirDelimiter); + { + NFind::CEnumeratorW enumerator(pathPrefix + UString(NName::kAnyStringWildcard)); + while(enumerator.Next(fileInfo)) + if (!RemoveDirectorySubItems2(pathPrefix, fileInfo)) + return false; + } + if (!MySetFileAttributes(path, 0)) + return false; + return MyRemoveDirectory(path); +} +#endif + +#ifndef _WIN32_WCE + +bool MyGetShortPathName(LPCTSTR longPath, CSysString &shortPath) +{ + DWORD needLength = ::GetShortPathName(longPath, shortPath.GetBuffer(MAX_PATH + 1), MAX_PATH + 1); + shortPath.ReleaseBuffer(); + return (needLength > 0 && needLength < MAX_PATH); +} + +bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath, int &fileNamePartStartIndex) +{ + resultPath.Empty(); + LPTSTR fileNamePointer = 0; + LPTSTR buffer = resultPath.GetBuffer(MAX_PATH); + DWORD needLength = ::GetFullPathName(fileName, MAX_PATH + 1, buffer, &fileNamePointer); + resultPath.ReleaseBuffer(); + if (needLength == 0) + return false; + if (needLength >= MAX_PATH) + { + #ifdef WIN_LONG_PATH2 + needLength++; + buffer = resultPath.GetBuffer(needLength + 1); + DWORD needLength2 = ::GetFullPathNameW(fileName, needLength, buffer, &fileNamePointer); + resultPath.ReleaseBuffer(); + if (needLength2 == 0 || needLength2 > needLength) + #endif + return false; + } + if (fileNamePointer == 0) + fileNamePartStartIndex = lstrlen(fileName); + else + fileNamePartStartIndex = (int)(fileNamePointer - buffer); + return true; +} + +#ifndef _UNICODE +bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath, int &fileNamePartStartIndex) +{ + resultPath.Empty(); + if (g_IsNT) + { + LPWSTR fileNamePointer = 0; + LPWSTR buffer = resultPath.GetBuffer(MAX_PATH); + DWORD needLength = ::GetFullPathNameW(fileName, MAX_PATH + 1, buffer, &fileNamePointer); + resultPath.ReleaseBuffer(); + if (needLength == 0) + return false; + if (needLength >= MAX_PATH) + { + #ifdef WIN_LONG_PATH + needLength++; + buffer = resultPath.GetBuffer(needLength + 1); + DWORD needLength2 = ::GetFullPathNameW(fileName, needLength, buffer, &fileNamePointer); + resultPath.ReleaseBuffer(); + if (needLength2 == 0 || needLength2 > needLength) + #endif + return false; + } + if (fileNamePointer == 0) + fileNamePartStartIndex = MyStringLen(fileName); + else + fileNamePartStartIndex = (int)(fileNamePointer - buffer); + } + else + { + CSysString sysPath; + if (!MyGetFullPathName(GetSysPath(fileName), sysPath, fileNamePartStartIndex)) + return false; + UString resultPath1 = GetUnicodePath(sysPath.Left(fileNamePartStartIndex)); + UString resultPath2 = GetUnicodePath(sysPath.Mid(fileNamePartStartIndex)); + fileNamePartStartIndex = resultPath1.Length(); + resultPath = resultPath1 + resultPath2; + } + return true; +} +#endif + + +bool MyGetFullPathName(LPCTSTR fileName, CSysString &path) +{ + int index; + return MyGetFullPathName(fileName, path, index); +} + +#ifndef _UNICODE +bool MyGetFullPathName(LPCWSTR fileName, UString &path) +{ + int index; + return MyGetFullPathName(fileName, path, index); +} +#endif + +bool GetOnlyName(LPCTSTR fileName, CSysString &resultName) +{ + int index; + if (!MyGetFullPathName(fileName, resultName, index)) + return false; + resultName = resultName.Mid(index); + return true; +} + +#ifndef _UNICODE +bool GetOnlyName(LPCWSTR fileName, UString &resultName) +{ + int index; + if (!MyGetFullPathName(fileName, resultName, index)) + return false; + resultName = resultName.Mid(index); + return true; +} +#endif + +bool GetOnlyDirPrefix(LPCTSTR fileName, CSysString &resultName) +{ + int index; + if (!MyGetFullPathName(fileName, resultName, index)) + return false; + resultName = resultName.Left(index); + return true; +} + +#ifndef _UNICODE +bool GetOnlyDirPrefix(LPCWSTR fileName, UString &resultName) +{ + int index; + if (!MyGetFullPathName(fileName, resultName, index)) + return false; + resultName = resultName.Left(index); + return true; +} +#endif + +bool MyGetCurrentDirectory(CSysString &path) +{ + DWORD needLength = ::GetCurrentDirectory(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1)); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); +} + +#ifndef _UNICODE +bool MySetCurrentDirectory(LPCWSTR path) +{ + if (g_IsNT) + return BOOLToBool(::SetCurrentDirectoryW(path)); + return MySetCurrentDirectory(GetSysPath(path)); +} +bool MyGetCurrentDirectory(UString &path) +{ + if (g_IsNT) + { + DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1)); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); + } + CSysString sysPath; + if (!MyGetCurrentDirectory(sysPath)) + return false; + path = GetUnicodePath(sysPath); + return true; +} +#endif +#endif + +bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension, + CSysString &resultPath, UINT32 &filePart) +{ + LPTSTR filePartPointer; + DWORD value = ::SearchPath(path, fileName, extension, + MAX_PATH, resultPath.GetBuffer(MAX_PATH + 1), &filePartPointer); + filePart = (UINT32)(filePartPointer - (LPCTSTR)resultPath); + resultPath.ReleaseBuffer(); + return (value > 0 && value <= MAX_PATH); +} + +#ifndef _UNICODE +bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension, + UString &resultPath, UINT32 &filePart) +{ + if (g_IsNT) + { + LPWSTR filePartPointer = 0; + DWORD value = ::SearchPathW(path, fileName, extension, + MAX_PATH, resultPath.GetBuffer(MAX_PATH + 1), &filePartPointer); + filePart = (UINT32)(filePartPointer - (LPCWSTR)resultPath); + resultPath.ReleaseBuffer(); + return (value > 0 && value <= MAX_PATH); + } + + CSysString sysPath; + if (!MySearchPath( + path != 0 ? (LPCTSTR)GetSysPath(path): 0, + fileName != 0 ? (LPCTSTR)GetSysPath(fileName): 0, + extension != 0 ? (LPCTSTR)GetSysPath(extension): 0, + sysPath, filePart)) + return false; + UString resultPath1 = GetUnicodePath(sysPath.Left(filePart)); + UString resultPath2 = GetUnicodePath(sysPath.Mid(filePart)); + filePart = resultPath1.Length(); + resultPath = resultPath1 + resultPath2; + return true; +} +#endif + +bool MyGetTempPath(CSysString &path) +{ + DWORD needLength = ::GetTempPath(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1)); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); +} + +#ifndef _UNICODE +bool MyGetTempPath(UString &path) +{ + path.Empty(); + if (g_IsNT) + { + DWORD needLength = ::GetTempPathW(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1)); + path.ReleaseBuffer(); + return (needLength > 0 && needLength <= MAX_PATH); + } + CSysString sysPath; + if (!MyGetTempPath(sysPath)) + return false; + path = GetUnicodePath(sysPath); + return true; +} +#endif + +UINT MyGetTempFileName(LPCTSTR dirPath, LPCTSTR prefix, CSysString &path) +{ + UINT number = ::GetTempFileName(dirPath, prefix, 0, path.GetBuffer(MAX_PATH + 1)); + path.ReleaseBuffer(); + return number; +} + +#ifndef _UNICODE +UINT MyGetTempFileName(LPCWSTR dirPath, LPCWSTR prefix, UString &path) +{ + if (g_IsNT) + { + UINT number = ::GetTempFileNameW(dirPath, prefix, 0, path.GetBuffer(MAX_PATH)); + path.ReleaseBuffer(); + return number; + } + CSysString sysPath; + UINT number = MyGetTempFileName( + dirPath ? (LPCTSTR)GetSysPath(dirPath): 0, + prefix ? (LPCTSTR)GetSysPath(prefix): 0, + sysPath); + path = GetUnicodePath(sysPath); + return number; +} +#endif + +UINT CTempFile::Create(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath) +{ + Remove(); + UINT number = MyGetTempFileName(dirPath, prefix, resultPath); + if(number != 0) + { + _fileName = resultPath; + _mustBeDeleted = true; + } + return number; +} + +bool CTempFile::Create(LPCTSTR prefix, CSysString &resultPath) +{ + CSysString tempPath; + if (!MyGetTempPath(tempPath)) + return false; + if (Create(tempPath, prefix, resultPath) != 0) + return true; + if (!MyGetWindowsDirectory(tempPath)) + return false; + return (Create(tempPath, prefix, resultPath) != 0); +} + +bool CTempFile::Remove() +{ + if (!_mustBeDeleted) + return true; + _mustBeDeleted = !DeleteFileAlways(_fileName); + return !_mustBeDeleted; +} + +#ifndef _UNICODE + +UINT CTempFileW::Create(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath) +{ + Remove(); + UINT number = MyGetTempFileName(dirPath, prefix, resultPath); + if(number != 0) + { + _fileName = resultPath; + _mustBeDeleted = true; + } + return number; +} + +bool CTempFileW::Create(LPCWSTR prefix, UString &resultPath) +{ + UString tempPath; + if (!MyGetTempPath(tempPath)) + return false; + if (Create(tempPath, prefix, resultPath) != 0) + return true; + if (!MyGetWindowsDirectory(tempPath)) + return false; + return (Create(tempPath, prefix, resultPath) != 0); +} + +bool CTempFileW::Remove() +{ + if (!_mustBeDeleted) + return true; + _mustBeDeleted = !DeleteFileAlways(_fileName); + return !_mustBeDeleted; +} + +#endif + +bool CreateTempDirectory(LPCTSTR prefix, CSysString &dirName) +{ + /* + CSysString prefix = tempPath + prefixChars; + CRandom random; + random.Init(); + */ + for (;;) + { + CTempFile tempFile; + if (!tempFile.Create(prefix, dirName)) + return false; + if (!::DeleteFile(dirName)) + return false; + /* + UINT32 randomNumber = random.Generate(); + TCHAR randomNumberString[32]; + _stprintf(randomNumberString, _T("%04X"), randomNumber); + dirName = prefix + randomNumberString; + */ + if(NFind::DoesFileExist(dirName)) + continue; + if (MyCreateDirectory(dirName)) + return true; + if (::GetLastError() != ERROR_ALREADY_EXISTS) + return false; + } +} + +bool CTempDirectory::Create(LPCTSTR prefix) +{ + Remove(); + return (_mustBeDeleted = CreateTempDirectory(prefix, _tempDir)); +} + +#ifndef _UNICODE + +bool CreateTempDirectory(LPCWSTR prefix, UString &dirName) +{ + /* + CSysString prefix = tempPath + prefixChars; + CRandom random; + random.Init(); + */ + for (;;) + { + CTempFileW tempFile; + if (!tempFile.Create(prefix, dirName)) + return false; + if (!DeleteFileAlways(dirName)) + return false; + /* + UINT32 randomNumber = random.Generate(); + TCHAR randomNumberString[32]; + _stprintf(randomNumberString, _T("%04X"), randomNumber); + dirName = prefix + randomNumberString; + */ + if(NFind::DoesFileExist(dirName)) + continue; + if (MyCreateDirectory(dirName)) + return true; + if (::GetLastError() != ERROR_ALREADY_EXISTS) + return false; + } +} + +bool CTempDirectoryW::Create(LPCWSTR prefix) +{ + Remove(); + return (_mustBeDeleted = CreateTempDirectory(prefix, _tempDir)); +} + +#endif + +}}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileDir.h b/3rdparty/physfs/lzma/CPP/Windows/FileDir.h new file mode 100644 index 0000000..b6f8135 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileDir.h @@ -0,0 +1,178 @@ +// Windows/FileDir.h + +#ifndef __WINDOWS_FILEDIR_H +#define __WINDOWS_FILEDIR_H + +#include "../Common/MyString.h" +#include "Defs.h" + +namespace NWindows { +namespace NFile { +namespace NDirectory { + +#ifdef WIN_LONG_PATH +bool GetLongPaths(LPCWSTR s1, LPCWSTR s2, UString &d1, UString &d2); +#endif + +bool MyGetWindowsDirectory(CSysString &path); +bool MyGetSystemDirectory(CSysString &path); +#ifndef _UNICODE +bool MyGetWindowsDirectory(UString &path); +bool MyGetSystemDirectory(UString &path); +#endif + +bool SetDirTime(LPCWSTR fileName, const FILETIME *creationTime, const FILETIME *lastAccessTime, const FILETIME *lastWriteTime); + +bool MySetFileAttributes(LPCTSTR fileName, DWORD fileAttributes); +bool MyMoveFile(LPCTSTR existFileName, LPCTSTR newFileName); +bool MyRemoveDirectory(LPCTSTR pathName); +bool MyCreateDirectory(LPCTSTR pathName); +bool CreateComplexDirectory(LPCTSTR pathName); +bool DeleteFileAlways(LPCTSTR name); +bool RemoveDirectoryWithSubItems(const CSysString &path); + +#ifndef _UNICODE +bool MySetFileAttributes(LPCWSTR fileName, DWORD fileAttributes); +bool MyMoveFile(LPCWSTR existFileName, LPCWSTR newFileName); +bool MyRemoveDirectory(LPCWSTR pathName); +bool MyCreateDirectory(LPCWSTR pathName); +bool CreateComplexDirectory(LPCWSTR pathName); +bool DeleteFileAlways(LPCWSTR name); +bool RemoveDirectoryWithSubItems(const UString &path); +#endif + +#ifndef _WIN32_WCE +bool MyGetShortPathName(LPCTSTR longPath, CSysString &shortPath); + +bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath, + int &fileNamePartStartIndex); +bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath); +bool GetOnlyName(LPCTSTR fileName, CSysString &resultName); +bool GetOnlyDirPrefix(LPCTSTR fileName, CSysString &resultName); +#ifndef _UNICODE +bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath, + int &fileNamePartStartIndex); +bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath); +bool GetOnlyName(LPCWSTR fileName, UString &resultName); +bool GetOnlyDirPrefix(LPCWSTR fileName, UString &resultName); +#endif + +inline bool MySetCurrentDirectory(LPCTSTR path) + { return BOOLToBool(::SetCurrentDirectory(path)); } +bool MyGetCurrentDirectory(CSysString &resultPath); +#ifndef _UNICODE +bool MySetCurrentDirectory(LPCWSTR path); +bool MyGetCurrentDirectory(UString &resultPath); +#endif +#endif + +bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension, + CSysString &resultPath, UINT32 &filePart); +#ifndef _UNICODE +bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension, + UString &resultPath, UINT32 &filePart); +#endif + +inline bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension, + CSysString &resultPath) +{ + UINT32 value; + return MySearchPath(path, fileName, extension, resultPath, value); +} + +#ifndef _UNICODE +inline bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension, + UString &resultPath) +{ + UINT32 value; + return MySearchPath(path, fileName, extension, resultPath, value); +} +#endif + +bool MyGetTempPath(CSysString &resultPath); +#ifndef _UNICODE +bool MyGetTempPath(UString &resultPath); +#endif + +UINT MyGetTempFileName(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath); +#ifndef _UNICODE +UINT MyGetTempFileName(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath); +#endif + +class CTempFile +{ + bool _mustBeDeleted; + CSysString _fileName; +public: + CTempFile(): _mustBeDeleted(false) {} + ~CTempFile() { Remove(); } + void DisableDeleting() { _mustBeDeleted = false; } + UINT Create(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath); + bool Create(LPCTSTR prefix, CSysString &resultPath); + bool Remove(); +}; + +#ifdef _UNICODE +typedef CTempFile CTempFileW; +#else +class CTempFileW +{ + bool _mustBeDeleted; + UString _fileName; +public: + CTempFileW(): _mustBeDeleted(false) {} + ~CTempFileW() { Remove(); } + void DisableDeleting() { _mustBeDeleted = false; } + UINT Create(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath); + bool Create(LPCWSTR prefix, UString &resultPath); + bool Remove(); +}; +#endif + +bool CreateTempDirectory(LPCTSTR prefixChars, CSysString &dirName); + +class CTempDirectory +{ + bool _mustBeDeleted; + CSysString _tempDir; +public: + const CSysString &GetPath() const { return _tempDir; } + CTempDirectory(): _mustBeDeleted(false) {} + ~CTempDirectory() { Remove(); } + bool Create(LPCTSTR prefix) ; + bool Remove() + { + if (!_mustBeDeleted) + return true; + _mustBeDeleted = !RemoveDirectoryWithSubItems(_tempDir); + return (!_mustBeDeleted); + } + void DisableDeleting() { _mustBeDeleted = false; } +}; + +#ifdef _UNICODE +typedef CTempDirectory CTempDirectoryW; +#else +class CTempDirectoryW +{ + bool _mustBeDeleted; + UString _tempDir; +public: + const UString &GetPath() const { return _tempDir; } + CTempDirectoryW(): _mustBeDeleted(false) {} + ~CTempDirectoryW() { Remove(); } + bool Create(LPCWSTR prefix) ; + bool Remove() + { + if (!_mustBeDeleted) + return true; + _mustBeDeleted = !RemoveDirectoryWithSubItems(_tempDir); + return (!_mustBeDeleted); + } + void DisableDeleting() { _mustBeDeleted = false; } +}; +#endif + +}}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileFind.cpp b/3rdparty/physfs/lzma/CPP/Windows/FileFind.cpp new file mode 100644 index 0000000..3b5bdf7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileFind.cpp @@ -0,0 +1,408 @@ +// Windows/FileFind.cpp + +#include "StdAfx.h" + +#include "FileFind.h" +#ifndef _UNICODE +#include "../Common/StringConvert.h" +#endif + +#ifndef _UNICODE +extern bool g_IsNT; +#endif + +namespace NWindows { +namespace NFile { + +#if defined(WIN_LONG_PATH) && defined(_UNICODE) +#define WIN_LONG_PATH2 +#endif + +bool GetLongPath(LPCWSTR fileName, UString &res); + +namespace NFind { + +static const TCHAR kDot = TEXT('.'); + +bool CFileInfo::IsDots() const +{ + if (!IsDirectory() || Name.IsEmpty()) + return false; + if (Name[0] != kDot) + return false; + return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); +} + +#ifndef _UNICODE +bool CFileInfoW::IsDots() const +{ + if (!IsDirectory() || Name.IsEmpty()) + return false; + if (Name[0] != kDot) + return false; + return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); +} +#endif + +static void ConvertWIN32_FIND_DATA_To_FileInfo( + const WIN32_FIND_DATA &findData, + CFileInfo &fileInfo) +{ + fileInfo.Attributes = findData.dwFileAttributes; + fileInfo.CreationTime = findData.ftCreationTime; + fileInfo.LastAccessTime = findData.ftLastAccessTime; + fileInfo.LastWriteTime = findData.ftLastWriteTime; + fileInfo.Size = (((UInt64)findData.nFileSizeHigh) << 32) + findData.nFileSizeLow; + fileInfo.Name = findData.cFileName; + #ifndef _WIN32_WCE + fileInfo.ReparseTag = findData.dwReserved0; + #else + fileInfo.ObjectID = findData.dwOID; + #endif +} + +#ifndef _UNICODE + +static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } + +static void ConvertWIN32_FIND_DATA_To_FileInfo( + const WIN32_FIND_DATAW &findData, + CFileInfoW &fileInfo) +{ + fileInfo.Attributes = findData.dwFileAttributes; + fileInfo.CreationTime = findData.ftCreationTime; + fileInfo.LastAccessTime = findData.ftLastAccessTime; + fileInfo.LastWriteTime = findData.ftLastWriteTime; + fileInfo.Size = (((UInt64)findData.nFileSizeHigh) << 32) + findData.nFileSizeLow; + fileInfo.Name = findData.cFileName; + #ifndef _WIN32_WCE + fileInfo.ReparseTag = findData.dwReserved0; + #else + fileInfo.ObjectID = findData.dwOID; + #endif +} + +static void ConvertWIN32_FIND_DATA_To_FileInfo( + const WIN32_FIND_DATA &findData, + CFileInfoW &fileInfo) +{ + fileInfo.Attributes = findData.dwFileAttributes; + fileInfo.CreationTime = findData.ftCreationTime; + fileInfo.LastAccessTime = findData.ftLastAccessTime; + fileInfo.LastWriteTime = findData.ftLastWriteTime; + fileInfo.Size = (((UInt64)findData.nFileSizeHigh) << 32) + findData.nFileSizeLow; + fileInfo.Name = GetUnicodeString(findData.cFileName, GetCurrentCodePage()); + #ifndef _WIN32_WCE + fileInfo.ReparseTag = findData.dwReserved0; + #else + fileInfo.ObjectID = findData.dwOID; + #endif +} +#endif + +//////////////////////////////// +// CFindFile + +bool CFindFile::Close() +{ + if (_handle == INVALID_HANDLE_VALUE) + return true; + if (!::FindClose(_handle)) + return false; + _handle = INVALID_HANDLE_VALUE; + return true; +} + + +bool CFindFile::FindFirst(LPCTSTR wildcard, CFileInfo &fileInfo) +{ + if (!Close()) + return false; + WIN32_FIND_DATA findData; + _handle = ::FindFirstFile(wildcard, &findData); + #ifdef WIN_LONG_PATH2 + if (_handle == INVALID_HANDLE_VALUE) + { + UString longPath; + if (GetLongPath(wildcard, longPath)) + _handle = ::FindFirstFileW(longPath, &findData); + } + #endif + if (_handle == INVALID_HANDLE_VALUE) + return false; + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + return true; +} + +#ifndef _UNICODE +bool CFindFile::FindFirst(LPCWSTR wildcard, CFileInfoW &fileInfo) +{ + if (!Close()) + return false; + if (g_IsNT) + { + WIN32_FIND_DATAW findData; + _handle = ::FindFirstFileW(wildcard, &findData); + #ifdef WIN_LONG_PATH + if (_handle == INVALID_HANDLE_VALUE) + { + UString longPath; + if (GetLongPath(wildcard, longPath)) + _handle = ::FindFirstFileW(longPath, &findData); + } + #endif + if (_handle != INVALID_HANDLE_VALUE) + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + } + else + { + WIN32_FIND_DATAA findData; + _handle = ::FindFirstFileA(UnicodeStringToMultiByte(wildcard, + GetCurrentCodePage()), &findData); + if (_handle != INVALID_HANDLE_VALUE) + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + } + return (_handle != INVALID_HANDLE_VALUE); +} +#endif + +bool CFindFile::FindNext(CFileInfo &fileInfo) +{ + WIN32_FIND_DATA findData; + bool result = BOOLToBool(::FindNextFile(_handle, &findData)); + if (result) + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + return result; +} + +#ifndef _UNICODE +bool CFindFile::FindNext(CFileInfoW &fileInfo) +{ + if (g_IsNT) + { + WIN32_FIND_DATAW findData; + if (!::FindNextFileW(_handle, &findData)) + return false; + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + } + else + { + WIN32_FIND_DATAA findData; + if (!::FindNextFileA(_handle, &findData)) + return false; + ConvertWIN32_FIND_DATA_To_FileInfo(findData, fileInfo); + } + return true; +} +#endif + +bool FindFile(LPCTSTR wildcard, CFileInfo &fileInfo) +{ + CFindFile finder; + return finder.FindFirst(wildcard, fileInfo); +} + +#ifndef _UNICODE +bool FindFile(LPCWSTR wildcard, CFileInfoW &fileInfo) +{ + CFindFile finder; + return finder.FindFirst(wildcard, fileInfo); +} +#endif + +bool DoesFileExist(LPCTSTR name) +{ + CFileInfo fileInfo; + return FindFile(name, fileInfo); +} + +#ifndef _UNICODE +bool DoesFileExist(LPCWSTR name) +{ + CFileInfoW fileInfo; + return FindFile(name, fileInfo); +} +#endif + +///////////////////////////////////// +// CEnumerator + +bool CEnumerator::NextAny(CFileInfo &fileInfo) +{ + if (_findFile.IsHandleAllocated()) + return _findFile.FindNext(fileInfo); + else + return _findFile.FindFirst(_wildcard, fileInfo); +} + +bool CEnumerator::Next(CFileInfo &fileInfo) +{ + for (;;) + { + if (!NextAny(fileInfo)) + return false; + if (!fileInfo.IsDots()) + return true; + } +} + +bool CEnumerator::Next(CFileInfo &fileInfo, bool &found) +{ + if (Next(fileInfo)) + { + found = true; + return true; + } + found = false; + return (::GetLastError() == ERROR_NO_MORE_FILES); +} + +#ifndef _UNICODE +bool CEnumeratorW::NextAny(CFileInfoW &fileInfo) +{ + if (_findFile.IsHandleAllocated()) + return _findFile.FindNext(fileInfo); + else + return _findFile.FindFirst(_wildcard, fileInfo); +} + +bool CEnumeratorW::Next(CFileInfoW &fileInfo) +{ + for (;;) + { + if (!NextAny(fileInfo)) + return false; + if (!fileInfo.IsDots()) + return true; + } +} + +bool CEnumeratorW::Next(CFileInfoW &fileInfo, bool &found) +{ + if (Next(fileInfo)) + { + found = true; + return true; + } + found = false; + return (::GetLastError() == ERROR_NO_MORE_FILES); +} + +#endif + +//////////////////////////////// +// CFindChangeNotification +// FindFirstChangeNotification can return 0. MSDN doesn't tell about it. + +bool CFindChangeNotification::Close() +{ + if (!IsHandleAllocated()) + return true; + if (!::FindCloseChangeNotification(_handle)) + return false; + _handle = INVALID_HANDLE_VALUE; + return true; +} + +HANDLE CFindChangeNotification::FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter) +{ + _handle = ::FindFirstChangeNotification(pathName, BoolToBOOL(watchSubtree), notifyFilter); + #ifdef WIN_LONG_PATH2 + if (!IsHandleAllocated()) + { + UString longPath; + if (GetLongPath(pathName, longPath)) + _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); + } + #endif + return _handle; +} + +#ifndef _UNICODE +HANDLE CFindChangeNotification::FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter) +{ + if (!g_IsNT) + return FindFirst(UnicodeStringToMultiByte(pathName, GetCurrentCodePage()), watchSubtree, notifyFilter); + _handle = ::FindFirstChangeNotificationW(pathName, BoolToBOOL(watchSubtree), notifyFilter); + #ifdef WIN_LONG_PATH + if (!IsHandleAllocated()) + { + UString longPath; + if (GetLongPath(pathName, longPath)) + _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); + } + #endif + return _handle; +} +#endif + +#ifndef _WIN32_WCE +bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings) +{ + driveStrings.Clear(); + UINT32 size = GetLogicalDriveStrings(0, NULL); + if (size == 0) + return false; + CSysString buffer; + UINT32 newSize = GetLogicalDriveStrings(size, buffer.GetBuffer(size)); + if (newSize == 0) + return false; + if (newSize > size) + return false; + CSysString string; + for(UINT32 i = 0; i < newSize; i++) + { + TCHAR c = buffer[i]; + if (c == TEXT('\0')) + { + driveStrings.Add(string); + string.Empty(); + } + else + string += c; + } + if (!string.IsEmpty()) + return false; + return true; +} + +#ifndef _UNICODE +bool MyGetLogicalDriveStrings(UStringVector &driveStrings) +{ + driveStrings.Clear(); + if (g_IsNT) + { + UINT32 size = GetLogicalDriveStringsW(0, NULL); + if (size == 0) + return false; + UString buffer; + UINT32 newSize = GetLogicalDriveStringsW(size, buffer.GetBuffer(size)); + if (newSize == 0) + return false; + if (newSize > size) + return false; + UString string; + for(UINT32 i = 0; i < newSize; i++) + { + WCHAR c = buffer[i]; + if (c == L'\0') + { + driveStrings.Add(string); + string.Empty(); + } + else + string += c; + } + return string.IsEmpty(); + } + CSysStringVector driveStringsA; + bool res = MyGetLogicalDriveStrings(driveStringsA); + for (int i = 0; i < driveStringsA.Size(); i++) + driveStrings.Add(GetUnicodeString(driveStringsA[i])); + return res; +} +#endif + +#endif + +}}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileFind.h b/3rdparty/physfs/lzma/CPP/Windows/FileFind.h new file mode 100644 index 0000000..87846fd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileFind.h @@ -0,0 +1,153 @@ +// Windows/FileFind.h + +#ifndef __WINDOWS_FILEFIND_H +#define __WINDOWS_FILEFIND_H + +#include "../Common/MyString.h" +#include "../Common/Types.h" +#include "FileName.h" +#include "Defs.h" + +namespace NWindows { +namespace NFile { +namespace NFind { + +namespace NAttributes +{ + inline bool IsReadOnly(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_READONLY) != 0; } + inline bool IsHidden(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_HIDDEN) != 0; } + inline bool IsSystem(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_SYSTEM) != 0; } + inline bool IsDirectory(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } + inline bool IsArchived(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_ARCHIVE) != 0; } + inline bool IsCompressed(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_COMPRESSED) != 0; } + inline bool IsEncrypted(DWORD attributes) { return (attributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; } +} + +class CFileInfoBase +{ + bool MatchesMask(UINT32 mask) const { return ((Attributes & mask) != 0); } +public: + DWORD Attributes; + FILETIME CreationTime; + FILETIME LastAccessTime; + FILETIME LastWriteTime; + UInt64 Size; + + #ifndef _WIN32_WCE + UINT32 ReparseTag; + #else + DWORD ObjectID; + #endif + + bool IsArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); } + bool IsCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); } + bool IsDirectory() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); } + bool IsEncrypted() const { return MatchesMask(FILE_ATTRIBUTE_ENCRYPTED); } + bool IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); } + bool IsNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); } + bool IsOffline() const { return MatchesMask(FILE_ATTRIBUTE_OFFLINE); } + bool IsReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); } + bool HasReparsePoint() const { return MatchesMask(FILE_ATTRIBUTE_REPARSE_POINT); } + bool IsSparse() const { return MatchesMask(FILE_ATTRIBUTE_SPARSE_FILE); } + bool IsSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); } + bool IsTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); } +}; + +class CFileInfo: public CFileInfoBase +{ +public: + CSysString Name; + bool IsDots() const; +}; + +#ifdef _UNICODE +typedef CFileInfo CFileInfoW; +#else +class CFileInfoW: public CFileInfoBase +{ +public: + UString Name; + bool IsDots() const; +}; +#endif + +class CFindFile +{ + friend class CEnumerator; + HANDLE _handle; +public: + bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE; } + CFindFile(): _handle(INVALID_HANDLE_VALUE) {} + ~CFindFile() { Close(); } + bool FindFirst(LPCTSTR wildcard, CFileInfo &fileInfo); + bool FindNext(CFileInfo &fileInfo); + #ifndef _UNICODE + bool FindFirst(LPCWSTR wildcard, CFileInfoW &fileInfo); + bool FindNext(CFileInfoW &fileInfo); + #endif + bool Close(); +}; + +bool FindFile(LPCTSTR wildcard, CFileInfo &fileInfo); + +bool DoesFileExist(LPCTSTR name); +#ifndef _UNICODE +bool FindFile(LPCWSTR wildcard, CFileInfoW &fileInfo); +bool DoesFileExist(LPCWSTR name); +#endif + +class CEnumerator +{ + CFindFile _findFile; + CSysString _wildcard; + bool NextAny(CFileInfo &fileInfo); +public: + CEnumerator(): _wildcard(NName::kAnyStringWildcard) {} + CEnumerator(const CSysString &wildcard): _wildcard(wildcard) {} + bool Next(CFileInfo &fileInfo); + bool Next(CFileInfo &fileInfo, bool &found); +}; + +#ifdef _UNICODE +typedef CEnumerator CEnumeratorW; +#else +class CEnumeratorW +{ + CFindFile _findFile; + UString _wildcard; + bool NextAny(CFileInfoW &fileInfo); +public: + CEnumeratorW(): _wildcard(NName::kAnyStringWildcard) {} + CEnumeratorW(const UString &wildcard): _wildcard(wildcard) {} + bool Next(CFileInfoW &fileInfo); + bool Next(CFileInfoW &fileInfo, bool &found); +}; +#endif + +class CFindChangeNotification +{ + HANDLE _handle; +public: + operator HANDLE () { return _handle; } + bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE && _handle != 0; } + CFindChangeNotification(): _handle(INVALID_HANDLE_VALUE) {} + ~CFindChangeNotification() { Close(); } + bool Close(); + HANDLE FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter); + #ifndef _UNICODE + HANDLE FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter); + #endif + bool FindNext() { return BOOLToBool(::FindNextChangeNotification(_handle)); } +}; + +#ifndef _WIN32_WCE +bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings); +#ifndef _UNICODE +bool MyGetLogicalDriveStrings(UStringVector &driveStrings); +#endif +#endif + +}}} + +#endif + diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileIO.cpp b/3rdparty/physfs/lzma/CPP/Windows/FileIO.cpp new file mode 100644 index 0000000..effd486 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileIO.cpp @@ -0,0 +1,318 @@ +// Windows/FileIO.cpp + +#include "StdAfx.h" + +#include "FileIO.h" +#include "Defs.h" +#ifdef WIN_LONG_PATH +#include "../Common/MyString.h" +#endif +#ifndef _UNICODE +#include "../Common/StringConvert.h" +#endif + +#ifndef _UNICODE +extern bool g_IsNT; +#endif + +namespace NWindows { +namespace NFile { + +#if defined(WIN_LONG_PATH) && defined(_UNICODE) +#define WIN_LONG_PATH2 +#endif + +#ifdef WIN_LONG_PATH +bool GetLongPathBase(LPCWSTR s, UString &res) +{ + res.Empty(); + int len = MyStringLen(s); + wchar_t c = s[0]; + if (len < 1 || c == L'\\' || c == L'.' && (len == 1 || len == 2 && s[1] == L'.')) + return true; + UString curDir; + bool isAbs = false; + if (len > 3) + isAbs = (s[1] == L':' && s[2] == L'\\' && (c >= L'a' && c <= L'z' || c >= L'A' && c <= L'Z')); + + if (!isAbs) + { + DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, curDir.GetBuffer(MAX_PATH + 1)); + curDir.ReleaseBuffer(); + if (needLength == 0 || needLength > MAX_PATH) + return false; + if (curDir[curDir.Length() - 1] != L'\\') + curDir += L'\\'; + } + res = UString(L"\\\\?\\") + curDir + s; + return true; +} + +bool GetLongPath(LPCWSTR path, UString &longPath) +{ + if (GetLongPathBase(path, longPath)) + return !longPath.IsEmpty(); + return false; +} +#endif + +namespace NIO { + +CFileBase::~CFileBase() { Close(); } + +bool CFileBase::Create(LPCTSTR fileName, DWORD desiredAccess, + DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) +{ + if (!Close()) + return false; + _handle = ::CreateFile(fileName, desiredAccess, shareMode, + (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, + flagsAndAttributes, (HANDLE)NULL); + #ifdef WIN_LONG_PATH2 + if (_handle == INVALID_HANDLE_VALUE) + { + UString longPath; + if (GetLongPath(fileName, longPath)) + _handle = ::CreateFileW(longPath, desiredAccess, shareMode, + (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, + flagsAndAttributes, (HANDLE)NULL); + } + #endif + return (_handle != INVALID_HANDLE_VALUE); +} + +#ifndef _UNICODE +bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess, + DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) +{ + if (!g_IsNT) + return Create(UnicodeStringToMultiByte(fileName, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP), + desiredAccess, shareMode, creationDisposition, flagsAndAttributes); + if (!Close()) + return false; + _handle = ::CreateFileW(fileName, desiredAccess, shareMode, + (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, + flagsAndAttributes, (HANDLE)NULL); + #ifdef WIN_LONG_PATH + if (_handle == INVALID_HANDLE_VALUE) + { + UString longPath; + if (GetLongPath(fileName, longPath)) + _handle = ::CreateFileW(longPath, desiredAccess, shareMode, + (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, + flagsAndAttributes, (HANDLE)NULL); + } + #endif + return (_handle != INVALID_HANDLE_VALUE); +} +#endif + +bool CFileBase::Close() +{ + if (_handle == INVALID_HANDLE_VALUE) + return true; + if (!::CloseHandle(_handle)) + return false; + _handle = INVALID_HANDLE_VALUE; + return true; +} + +bool CFileBase::GetPosition(UInt64 &position) const +{ + return Seek(0, FILE_CURRENT, position); +} + +bool CFileBase::GetLength(UInt64 &length) const +{ + DWORD sizeHigh; + DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh); + if(sizeLow == 0xFFFFFFFF) + if(::GetLastError() != NO_ERROR) + return false; + length = (((UInt64)sizeHigh) << 32) + sizeLow; + return true; +} + +bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const +{ + LARGE_INTEGER value; + value.QuadPart = distanceToMove; + value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod); + if (value.LowPart == 0xFFFFFFFF) + if(::GetLastError() != NO_ERROR) + return false; + newPosition = value.QuadPart; + return true; +} + +bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) +{ + return Seek(position, FILE_BEGIN, newPosition); +} + +bool CFileBase::SeekToBegin() +{ + UInt64 newPosition; + return Seek(0, newPosition); +} + +bool CFileBase::SeekToEnd(UInt64 &newPosition) +{ + return Seek(0, FILE_END, newPosition); +} + +bool CFileBase::GetFileInformation(CByHandleFileInfo &fileInfo) const +{ + BY_HANDLE_FILE_INFORMATION winFileInfo; + if(!::GetFileInformationByHandle(_handle, &winFileInfo)) + return false; + fileInfo.Attributes = winFileInfo.dwFileAttributes; + fileInfo.CreationTime = winFileInfo.ftCreationTime; + fileInfo.LastAccessTime = winFileInfo.ftLastAccessTime; + fileInfo.LastWriteTime = winFileInfo.ftLastWriteTime; + fileInfo.VolumeSerialNumber = winFileInfo.dwFileAttributes; + fileInfo.Size = (((UInt64)winFileInfo.nFileSizeHigh) << 32) + winFileInfo.nFileSizeLow; + fileInfo.NumberOfLinks = winFileInfo.nNumberOfLinks; + fileInfo.FileIndex = (((UInt64)winFileInfo.nFileIndexHigh) << 32) + winFileInfo.nFileIndexLow; + return true; +} + +///////////////////////// +// CInFile + +bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) + { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } + +bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite) +{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } + +bool CInFile::Open(LPCTSTR fileName) + { return OpenShared(fileName, false); } + +#ifndef _UNICODE +bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) + { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } + +bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite) +{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } + +bool CInFile::Open(LPCWSTR fileName) + { return OpenShared(fileName, false); } +#endif + +// ReadFile and WriteFile functions in Windows have BUG: +// If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) +// from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES +// (Insufficient system resources exist to complete the requested service). + +// Probably in some version of Windows there are problems with other sizes: +// for 32 MB (maybe also for 16 MB). +// And message can be "Network connection was lost" + +static UInt32 kChunkSizeMax = (1 << 22); + +bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) +{ + if (size > kChunkSizeMax) + size = kChunkSizeMax; + DWORD processedLoc = 0; + bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); + processedSize = (UInt32)processedLoc; + return res; +} + +bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) +{ + processedSize = 0; + do + { + UInt32 processedLoc = 0; + bool res = ReadPart(data, size, processedLoc); + processedSize += processedLoc; + if (!res) + return false; + if (processedLoc == 0) + return true; + data = (void *)((unsigned char *)data + processedLoc); + size -= processedLoc; + } + while (size > 0); + return true; +} + +///////////////////////// +// COutFile + +bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) + { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } + +static inline DWORD GetCreationDisposition(bool createAlways) + { return createAlways? CREATE_ALWAYS: CREATE_NEW; } + +bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition) + { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } + +bool COutFile::Create(LPCTSTR fileName, bool createAlways) + { return Open(fileName, GetCreationDisposition(createAlways)); } + +#ifndef _UNICODE + +bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) + { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } + +bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition) + { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } + +bool COutFile::Create(LPCWSTR fileName, bool createAlways) + { return Open(fileName, GetCreationDisposition(createAlways)); } + +#endif + +bool COutFile::SetTime(const FILETIME *creationTime, const FILETIME *lastAccessTime, const FILETIME *lastWriteTime) + { return BOOLToBool(::SetFileTime(_handle, creationTime, lastAccessTime, lastWriteTime)); } + +bool COutFile::SetLastWriteTime(const FILETIME *lastWriteTime) + { return SetTime(NULL, NULL, lastWriteTime); } + +bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) +{ + if (size > kChunkSizeMax) + size = kChunkSizeMax; + DWORD processedLoc = 0; + bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); + processedSize = (UInt32)processedLoc; + return res; +} + +bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) +{ + processedSize = 0; + do + { + UInt32 processedLoc = 0; + bool res = WritePart(data, size, processedLoc); + processedSize += processedLoc; + if (!res) + return false; + if (processedLoc == 0) + return true; + data = (const void *)((const unsigned char *)data + processedLoc); + size -= processedLoc; + } + while (size > 0); + return true; +} + +bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); } + +bool COutFile::SetLength(UInt64 length) +{ + UInt64 newPosition; + if(!Seek(length, newPosition)) + return false; + if(newPosition != length) + return false; + return SetEndOfFile(); +} + +}}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileIO.h b/3rdparty/physfs/lzma/CPP/Windows/FileIO.h new file mode 100644 index 0000000..a7ee880 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileIO.h @@ -0,0 +1,99 @@ +// Windows/FileIO.h + +#ifndef __WINDOWS_FILEIO_H +#define __WINDOWS_FILEIO_H + +#include "../Common/Types.h" + +namespace NWindows { +namespace NFile { +namespace NIO { + +struct CByHandleFileInfo +{ + DWORD Attributes; + FILETIME CreationTime; + FILETIME LastAccessTime; + FILETIME LastWriteTime; + DWORD VolumeSerialNumber; + UInt64 Size; + DWORD NumberOfLinks; + UInt64 FileIndex; +}; + +class CFileBase +{ +protected: + HANDLE _handle; + bool Create(LPCTSTR fileName, DWORD desiredAccess, + DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + #ifndef _UNICODE + bool Create(LPCWSTR fileName, DWORD desiredAccess, + DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + #endif + +public: + CFileBase(): _handle(INVALID_HANDLE_VALUE){}; + ~CFileBase(); + + bool Close(); + + bool GetPosition(UInt64 &position) const; + bool GetLength(UInt64 &length) const; + + bool Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const; + bool Seek(UInt64 position, UInt64 &newPosition); + bool SeekToBegin(); + bool SeekToEnd(UInt64 &newPosition); + + bool GetFileInformation(CByHandleFileInfo &fileInfo) const; +}; + +class CInFile: public CFileBase +{ +public: + bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + bool OpenShared(LPCTSTR fileName, bool shareForWrite); + bool Open(LPCTSTR fileName); + #ifndef _UNICODE + bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + bool OpenShared(LPCWSTR fileName, bool shareForWrite); + bool Open(LPCWSTR fileName); + #endif + bool ReadPart(void *data, UInt32 size, UInt32 &processedSize); + bool Read(void *data, UInt32 size, UInt32 &processedSize); +}; + +class COutFile: public CFileBase +{ + // DWORD m_CreationDisposition; +public: + // COutFile(): m_CreationDisposition(CREATE_NEW){}; + bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + bool Open(LPCTSTR fileName, DWORD creationDisposition); + bool Create(LPCTSTR fileName, bool createAlways); + + #ifndef _UNICODE + bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); + bool Open(LPCWSTR fileName, DWORD creationDisposition); + bool Create(LPCWSTR fileName, bool createAlways); + #endif + + /* + void SetOpenCreationDisposition(DWORD creationDisposition) + { m_CreationDisposition = creationDisposition; } + void SetOpenCreationDispositionCreateAlways() + { m_CreationDisposition = CREATE_ALWAYS; } + */ + + bool SetTime(const FILETIME *creationTime, const FILETIME *lastAccessTime, const FILETIME *lastWriteTime); + bool SetLastWriteTime(const FILETIME *lastWriteTime); + bool WritePart(const void *data, UInt32 size, UInt32 &processedSize); + bool Write(const void *data, UInt32 size, UInt32 &processedSize); + bool SetEndOfFile(); + bool SetLength(UInt64 length); +}; + +}}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileMapping.cpp b/3rdparty/physfs/lzma/CPP/Windows/FileMapping.cpp new file mode 100644 index 0000000..d884afb --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileMapping.cpp @@ -0,0 +1,14 @@ +// Windows/FileMapping.cpp + +#include "StdAfx.h" + +#include "Windows/FileMapping.h" + +namespace NWindows { +namespace NFile { +namespace NMapping { + + + + +}}} \ No newline at end of file diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileMapping.h b/3rdparty/physfs/lzma/CPP/Windows/FileMapping.h new file mode 100644 index 0000000..22db8f1 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileMapping.h @@ -0,0 +1,50 @@ +// Windows/FileMapping.h + +#ifndef __WINDOWS_FILEMAPPING_H +#define __WINDOWS_FILEMAPPING_H + +#include "Windows/Handle.h" +#include "Windows/Defs.h" + +namespace NWindows { +// namespace NFile { +// namespace NMapping { + +class CFileMapping: public CHandle +{ +public: + bool Create(HANDLE file, LPSECURITY_ATTRIBUTES attributes, + DWORD protect, UINT64 maximumSize, LPCTSTR name) + { + _handle = ::CreateFileMapping(file, attributes, + protect, DWORD(maximumSize >> 32), DWORD(maximumSize), name); + return (_handle != NULL); + } + + bool Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) + { + _handle = ::OpenFileMapping(desiredAccess, BoolToBOOL(inheritHandle), name); + return (_handle != NULL); + } + + LPVOID MapViewOfFile(DWORD desiredAccess, UINT64 fileOffset, + SIZE_T numberOfBytesToMap) + { + return ::MapViewOfFile(_handle, desiredAccess, + DWORD(fileOffset >> 32), DWORD(fileOffset), numberOfBytesToMap); + } + + LPVOID MapViewOfFileEx(DWORD desiredAccess, UINT64 fileOffset, + SIZE_T numberOfBytesToMap, LPVOID baseAddress) + { + return ::MapViewOfFileEx(_handle, desiredAccess, + DWORD(fileOffset >> 32), DWORD(fileOffset), + numberOfBytesToMap, baseAddress); + } + + +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileName.cpp b/3rdparty/physfs/lzma/CPP/Windows/FileName.cpp new file mode 100644 index 0000000..57c357f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileName.cpp @@ -0,0 +1,54 @@ +// Windows/FileName.cpp + +#include "StdAfx.h" + +#include "Windows/FileName.h" +#include "Common/Wildcard.h" + +namespace NWindows { +namespace NFile { +namespace NName { + +void NormalizeDirPathPrefix(CSysString &dirPath) +{ + if (dirPath.IsEmpty()) + return; + if (dirPath.ReverseFind(kDirDelimiter) != dirPath.Length() - 1) + dirPath += kDirDelimiter; +} + +#ifndef _UNICODE +void NormalizeDirPathPrefix(UString &dirPath) +{ + if (dirPath.IsEmpty()) + return; + if (dirPath.ReverseFind(wchar_t(kDirDelimiter)) != dirPath.Length() - 1) + dirPath += wchar_t(kDirDelimiter); +} +#endif + +#ifdef _WIN32 + +const wchar_t kExtensionDelimiter = L'.'; + +void SplitNameToPureNameAndExtension(const UString &fullName, + UString &pureName, UString &extensionDelimiter, UString &extension) +{ + int index = fullName.ReverseFind(kExtensionDelimiter); + if (index < 0) + { + pureName = fullName; + extensionDelimiter.Empty(); + extension.Empty(); + } + else + { + pureName = fullName.Left(index); + extensionDelimiter = kExtensionDelimiter; + extension = fullName.Mid(index + 1); + } +} + +#endif + +}}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/FileName.h b/3rdparty/physfs/lzma/CPP/Windows/FileName.h new file mode 100644 index 0000000..2eab267 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/FileName.h @@ -0,0 +1,27 @@ +// Windows/FileName.h + +#ifndef __WINDOWS_FILENAME_H +#define __WINDOWS_FILENAME_H + +#include "../Common/MyString.h" + +namespace NWindows { +namespace NFile { +namespace NName { + +const TCHAR kDirDelimiter = CHAR_PATH_SEPARATOR; +const TCHAR kAnyStringWildcard = '*'; + +void NormalizeDirPathPrefix(CSysString &dirPath); // ensures that it ended with '\\' +#ifndef _UNICODE +void NormalizeDirPathPrefix(UString &dirPath); // ensures that it ended with '\\' +#endif + +#ifdef _WIN32 +void SplitNameToPureNameAndExtension(const UString &fullName, + UString &pureName, UString &extensionDelimiter, UString &extension); +#endif + +}}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Handle.h b/3rdparty/physfs/lzma/CPP/Windows/Handle.h new file mode 100644 index 0000000..d4d8aae --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Handle.h @@ -0,0 +1,37 @@ +// Windows/Handle.h + +#ifndef __WINDOWS_HANDLE_H +#define __WINDOWS_HANDLE_H + +namespace NWindows { + +class CHandle +{ +protected: + HANDLE _handle; +public: + operator HANDLE() { return _handle; } + CHandle(): _handle(NULL) {} + ~CHandle() { Close(); } + bool Close() + { + if (_handle == NULL) + return true; + if (!::CloseHandle(_handle)) + return false; + _handle = NULL; + return true; + } + void Attach(HANDLE handle) + { _handle = handle; } + HANDLE Detach() + { + HANDLE handle = _handle; + _handle = NULL; + return handle; + } +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.cpp b/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.cpp new file mode 100644 index 0000000..b468e52 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.cpp @@ -0,0 +1,78 @@ +// Common/MemoryLock.cpp + +#include "StdAfx.h" + +namespace NWindows { +namespace NSecurity { + +#ifndef _UNICODE +typedef BOOL (WINAPI * OpenProcessTokenP)(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle); +typedef BOOL (WINAPI * LookupPrivilegeValueP)(LPCTSTR lpSystemName, LPCTSTR lpName, PLUID lpLuid); +typedef BOOL (WINAPI * AdjustTokenPrivilegesP)(HANDLE TokenHandle, BOOL DisableAllPrivileges, + PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength); +#endif + +#ifdef _UNICODE +bool EnableLockMemoryPrivilege( +#else +static bool EnableLockMemoryPrivilege2(HMODULE hModule, +#endif +bool enable) +{ + #ifndef _UNICODE + if (hModule == NULL) + return false; + OpenProcessTokenP openProcessToken = (OpenProcessTokenP)GetProcAddress(hModule, "OpenProcessToken"); + LookupPrivilegeValueP lookupPrivilegeValue = (LookupPrivilegeValueP)GetProcAddress(hModule, "LookupPrivilegeValueA" ); + AdjustTokenPrivilegesP adjustTokenPrivileges = (AdjustTokenPrivilegesP)GetProcAddress(hModule, "AdjustTokenPrivileges"); + if (openProcessToken == NULL || adjustTokenPrivileges == NULL || lookupPrivilegeValue == NULL) + return false; + #endif + + HANDLE token; + if (! + #ifdef _UNICODE + ::OpenProcessToken + #else + openProcessToken + #endif + (::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) + return false; + TOKEN_PRIVILEGES tp; + bool res = false; + if ( + #ifdef _UNICODE + ::LookupPrivilegeValue + #else + lookupPrivilegeValue + #endif + (NULL, SE_LOCK_MEMORY_NAME, &(tp.Privileges[0].Luid))) + { + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED: 0; + if ( + #ifdef _UNICODE + ::AdjustTokenPrivileges + #else + adjustTokenPrivileges + #endif + (token, FALSE, &tp, 0, NULL, NULL)) + res = (GetLastError() == ERROR_SUCCESS); + } + ::CloseHandle(token); + return res; +} + +#ifndef _UNICODE +bool EnableLockMemoryPrivilege(bool enable) +{ + HMODULE hModule = LoadLibrary(TEXT("Advapi32.dll")); + if(hModule == NULL) + return false; + bool res = EnableLockMemoryPrivilege2(hModule, enable); + ::FreeLibrary(hModule); + return res; +} +#endif + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.h b/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.h new file mode 100644 index 0000000..03a88b4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/MemoryLock.h @@ -0,0 +1,13 @@ +// Windows/MemoryLock.h + +#ifndef __WINDOWS_MEMORYLOCK_H +#define __WINDOWS_MEMORYLOCK_H + +namespace NWindows { +namespace NSecurity { + +bool EnableLockMemoryPrivilege(bool enable = true); + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/PropVariant.cpp b/3rdparty/physfs/lzma/CPP/Windows/PropVariant.cpp new file mode 100644 index 0000000..690e2b6 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/PropVariant.cpp @@ -0,0 +1,312 @@ +// Windows/PropVariant.cpp + +#include "StdAfx.h" + +#include "PropVariant.h" + +#include "../Common/Defs.h" + +namespace NWindows { +namespace NCOM { + +CPropVariant::CPropVariant(const PROPVARIANT& varSrc) +{ + vt = VT_EMPTY; + InternalCopy(&varSrc); +} + +CPropVariant::CPropVariant(const CPropVariant& varSrc) +{ + vt = VT_EMPTY; + InternalCopy(&varSrc); +} + +CPropVariant::CPropVariant(BSTR bstrSrc) +{ + vt = VT_EMPTY; + *this = bstrSrc; +} + +CPropVariant::CPropVariant(LPCOLESTR lpszSrc) +{ + vt = VT_EMPTY; + *this = lpszSrc; +} + +CPropVariant& CPropVariant::operator=(const CPropVariant& varSrc) +{ + InternalCopy(&varSrc); + return *this; +} +CPropVariant& CPropVariant::operator=(const PROPVARIANT& varSrc) +{ + InternalCopy(&varSrc); + return *this; +} + +CPropVariant& CPropVariant::operator=(BSTR bstrSrc) +{ + *this = (LPCOLESTR)bstrSrc; + return *this; +} + +CPropVariant& CPropVariant::operator=(LPCOLESTR lpszSrc) +{ + InternalClear(); + vt = VT_BSTR; + wReserved1 = 0; + bstrVal = ::SysAllocString(lpszSrc); + if (bstrVal == NULL && lpszSrc != NULL) + { + vt = VT_ERROR; + scode = E_OUTOFMEMORY; + } + return *this; +} + + +CPropVariant& CPropVariant::operator=(bool bSrc) +{ + if (vt != VT_BOOL) + { + InternalClear(); + vt = VT_BOOL; + } + boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE; + return *this; +} + +CPropVariant& CPropVariant::operator=(UInt32 value) +{ + if (vt != VT_UI4) + { + InternalClear(); + vt = VT_UI4; + } + ulVal = value; + return *this; +} + +CPropVariant& CPropVariant::operator=(UInt64 value) +{ + if (vt != VT_UI8) + { + InternalClear(); + vt = VT_UI8; + } + uhVal.QuadPart = value; + return *this; +} + +CPropVariant& CPropVariant::operator=(const FILETIME &value) +{ + if (vt != VT_FILETIME) + { + InternalClear(); + vt = VT_FILETIME; + } + filetime = value; + return *this; +} + +CPropVariant& CPropVariant::operator=(Int32 value) +{ + if (vt != VT_I4) + { + InternalClear(); + vt = VT_I4; + } + lVal = value; + + return *this; +} + +CPropVariant& CPropVariant::operator=(Byte value) +{ + if (vt != VT_UI1) + { + InternalClear(); + vt = VT_UI1; + } + bVal = value; + return *this; +} + +CPropVariant& CPropVariant::operator=(Int16 value) +{ + if (vt != VT_I2) + { + InternalClear(); + vt = VT_I2; + } + iVal = value; + return *this; +} + +/* +CPropVariant& CPropVariant::operator=(LONG value) +{ + if (vt != VT_I4) + { + InternalClear(); + vt = VT_I4; + } + lVal = value; + return *this; +} +*/ + +static HRESULT MyPropVariantClear(PROPVARIANT *propVariant) +{ + switch(propVariant->vt) + { + case VT_UI1: + case VT_I1: + case VT_I2: + case VT_UI2: + case VT_BOOL: + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_INT: + case VT_UINT: + case VT_ERROR: + case VT_FILETIME: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + propVariant->vt = VT_EMPTY; + propVariant->wReserved1 = 0; + return S_OK; + } + return ::VariantClear((VARIANTARG *)propVariant); +} + +HRESULT CPropVariant::Clear() +{ + return MyPropVariantClear(this); +} + +HRESULT CPropVariant::Copy(const PROPVARIANT* pSrc) +{ + ::VariantClear((tagVARIANT *)this); + switch(pSrc->vt) + { + case VT_UI1: + case VT_I1: + case VT_I2: + case VT_UI2: + case VT_BOOL: + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_INT: + case VT_UINT: + case VT_ERROR: + case VT_FILETIME: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + memmove((PROPVARIANT*)this, pSrc, sizeof(PROPVARIANT)); + return S_OK; + } + return ::VariantCopy((tagVARIANT *)this, (tagVARIANT *)(pSrc)); +} + + +HRESULT CPropVariant::Attach(PROPVARIANT* pSrc) +{ + HRESULT hr = Clear(); + if (FAILED(hr)) + return hr; + memcpy(this, pSrc, sizeof(PROPVARIANT)); + pSrc->vt = VT_EMPTY; + return S_OK; +} + +HRESULT CPropVariant::Detach(PROPVARIANT* pDest) +{ + HRESULT hr = MyPropVariantClear(pDest); + if (FAILED(hr)) + return hr; + memcpy(pDest, this, sizeof(PROPVARIANT)); + vt = VT_EMPTY; + return S_OK; +} + +HRESULT CPropVariant::InternalClear() +{ + HRESULT hr = Clear(); + if (FAILED(hr)) + { + vt = VT_ERROR; + scode = hr; + } + return hr; +} + +void CPropVariant::InternalCopy(const PROPVARIANT* pSrc) +{ + HRESULT hr = Copy(pSrc); + if (FAILED(hr)) + { + vt = VT_ERROR; + scode = hr; + } +} + +int CPropVariant::Compare(const CPropVariant &a) +{ + if(vt != a.vt) + return 0; // it's mean some bug + switch (vt) + { + case VT_EMPTY: + return 0; + + /* + case VT_I1: + return MyCompare(cVal, a.cVal); + */ + case VT_UI1: + return MyCompare(bVal, a.bVal); + + case VT_I2: + return MyCompare(iVal, a.iVal); + case VT_UI2: + return MyCompare(uiVal, a.uiVal); + + case VT_I4: + return MyCompare(lVal, a.lVal); + /* + case VT_INT: + return MyCompare(intVal, a.intVal); + */ + case VT_UI4: + return MyCompare(ulVal, a.ulVal); + /* + case VT_UINT: + return MyCompare(uintVal, a.uintVal); + */ + case VT_I8: + return MyCompare(hVal.QuadPart, a.hVal.QuadPart); + case VT_UI8: + return MyCompare(uhVal.QuadPart, a.uhVal.QuadPart); + + case VT_BOOL: + return -MyCompare(boolVal, a.boolVal); + + case VT_FILETIME: + return ::CompareFileTime(&filetime, &a.filetime); + case VT_BSTR: + return 0; // Not implemented + // return MyCompare(aPropVarint.cVal); + + default: + return 0; + } +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/PropVariant.h b/3rdparty/physfs/lzma/CPP/Windows/PropVariant.h new file mode 100644 index 0000000..d44215f --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/PropVariant.h @@ -0,0 +1,57 @@ +// Windows/PropVariant.h + +#ifndef __WINDOWS_PROPVARIANT_H +#define __WINDOWS_PROPVARIANT_H + +#include "../Common/MyWindows.h" +#include "../Common/Types.h" + +namespace NWindows { +namespace NCOM { + +class CPropVariant : public tagPROPVARIANT +{ +public: + CPropVariant() { vt = VT_EMPTY; wReserved1 = 0; } + ~CPropVariant() { Clear(); } + CPropVariant(const PROPVARIANT& varSrc); + CPropVariant(const CPropVariant& varSrc); + CPropVariant(BSTR bstrSrc); + CPropVariant(LPCOLESTR lpszSrc); + CPropVariant(bool bSrc) { vt = VT_BOOL; wReserved1 = 0; boolVal = (bSrc ? VARIANT_TRUE : VARIANT_FALSE); }; + CPropVariant(UInt32 value) { vt = VT_UI4; wReserved1 = 0; ulVal = value; } + CPropVariant(UInt64 value) { vt = VT_UI8; wReserved1 = 0; uhVal = *(ULARGE_INTEGER*)&value; } + CPropVariant(const FILETIME &value) { vt = VT_FILETIME; wReserved1 = 0; filetime = value; } + CPropVariant(Int32 value) { vt = VT_I4; wReserved1 = 0; lVal = value; } + CPropVariant(Byte value) { vt = VT_UI1; wReserved1 = 0; bVal = value; } + CPropVariant(Int16 value) { vt = VT_I2; wReserved1 = 0; iVal = value; } + // CPropVariant(LONG value, VARTYPE vtSrc = VT_I4) { vt = vtSrc; lVal = value; } + + CPropVariant& operator=(const CPropVariant& varSrc); + CPropVariant& operator=(const PROPVARIANT& varSrc); + CPropVariant& operator=(BSTR bstrSrc); + CPropVariant& operator=(LPCOLESTR lpszSrc); + CPropVariant& operator=(bool bSrc); + CPropVariant& operator=(UInt32 value); + CPropVariant& operator=(UInt64 value); + CPropVariant& operator=(const FILETIME &value); + + CPropVariant& operator=(Int32 value); + CPropVariant& operator=(Byte value); + CPropVariant& operator=(Int16 value); + // CPropVariant& operator=(LONG value); + + HRESULT Clear(); + HRESULT Copy(const PROPVARIANT* pSrc); + HRESULT Attach(PROPVARIANT* pSrc); + HRESULT Detach(PROPVARIANT* pDest); + + HRESULT InternalClear(); + void InternalCopy(const PROPVARIANT* pSrc); + + int Compare(const CPropVariant &a1); +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.cpp b/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.cpp new file mode 100644 index 0000000..993dac7 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.cpp @@ -0,0 +1,150 @@ +// PropVariantConversions.cpp + +#include "StdAfx.h" + +// #include + +#include "PropVariantConversions.h" + +#include "Windows/Defs.h" + +#include "Common/StringConvert.h" +#include "Common/IntToString.h" + +static UString ConvertUInt64ToString(UInt64 value) +{ + wchar_t buffer[32]; + ConvertUInt64ToString(value, buffer); + return buffer; +} + +static UString ConvertInt64ToString(Int64 value) +{ + wchar_t buffer[32]; + ConvertInt64ToString(value, buffer); + return buffer; +} + +static char *UIntToStringSpec(UInt32 value, char *s, int numPos) +{ + char temp[16]; + int pos = 0; + do + { + temp[pos++] = (char)('0' + value % 10); + value /= 10; + } + while (value != 0); + int i; + for (i = 0; i < numPos - pos; i++) + *s++ = '0'; + do + *s++ = temp[--pos]; + while (pos > 0); + *s = '\0'; + return s; +} + +bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime, bool includeSeconds) +{ + s[0] = '\0'; + SYSTEMTIME st; + if(!BOOLToBool(FileTimeToSystemTime(&ft, &st))) + return false; + s = UIntToStringSpec(st.wYear, s, 4); + *s++ = '-'; + s = UIntToStringSpec(st.wMonth, s, 2); + *s++ = '-'; + s = UIntToStringSpec(st.wDay, s, 2); + if (includeTime) + { + *s++ = ' '; + s = UIntToStringSpec(st.wHour, s, 2); + *s++ = ':'; + s = UIntToStringSpec(st.wMinute, s, 2); + if (includeSeconds) + { + *s++ = ':'; + UIntToStringSpec(st.wSecond, s, 2); + } + } + /* + sprintf(s, "%04d-%02d-%02d", st.wYear, st.wMonth, st.wDay); + if (includeTime) + { + sprintf(s + strlen(s), " %02d:%02d", st.wHour, st.wMinute); + if (includeSeconds) + sprintf(s + strlen(s), ":%02d", st.wSecond); + } + */ + return true; +} + +UString ConvertFileTimeToString(const FILETIME &fileTime, bool includeTime, bool includeSeconds) +{ + char s[32]; + ConvertFileTimeToString(fileTime, s, includeTime, includeSeconds); + return GetUnicodeString(s); +} + + +UString ConvertPropVariantToString(const PROPVARIANT &propVariant) +{ + switch (propVariant.vt) + { + case VT_EMPTY: + return UString(); + case VT_BSTR: + return propVariant.bstrVal; + case VT_UI1: + return ConvertUInt64ToString(propVariant.bVal); + case VT_UI2: + return ConvertUInt64ToString(propVariant.uiVal); + case VT_UI4: + return ConvertUInt64ToString(propVariant.ulVal); + case VT_UI8: + return ConvertUInt64ToString(propVariant.uhVal.QuadPart); + case VT_FILETIME: + return ConvertFileTimeToString(propVariant.filetime, true, true); + /* + case VT_I1: + return ConvertInt64ToString(propVariant.cVal); + */ + case VT_I2: + return ConvertInt64ToString(propVariant.iVal); + case VT_I4: + return ConvertInt64ToString(propVariant.lVal); + case VT_I8: + return ConvertInt64ToString(propVariant.hVal.QuadPart); + + case VT_BOOL: + return VARIANT_BOOLToBool(propVariant.boolVal) ? L"+" : L"-"; + default: + #ifndef _WIN32_WCE + throw 150245; + #else + return UString(); + #endif + } +} + +UInt64 ConvertPropVariantToUInt64(const PROPVARIANT &propVariant) +{ + switch (propVariant.vt) + { + case VT_UI1: + return propVariant.bVal; + case VT_UI2: + return propVariant.uiVal; + case VT_UI4: + return propVariant.ulVal; + case VT_UI8: + return (UInt64)propVariant.uhVal.QuadPart; + default: + #ifndef _WIN32_WCE + throw 151199; + #else + return 0; + #endif + } +} diff --git a/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.h b/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.h new file mode 100644 index 0000000..68ad961 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/PropVariantConversions.h @@ -0,0 +1,14 @@ +// Windows/PropVariantConversions.h + +#ifndef __PROPVARIANTCONVERSIONS_H +#define __PROPVARIANTCONVERSIONS_H + +#include "Common/Types.h" +#include "Common/MyString.h" + +bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime = true, bool includeSeconds = true); +UString ConvertFileTimeToString(const FILETIME &ft, bool includeTime = true, bool includeSeconds = true); +UString ConvertPropVariantToString(const PROPVARIANT &propVariant); +UInt64 ConvertPropVariantToUInt64(const PROPVARIANT &propVariant); + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/StdAfx.h b/3rdparty/physfs/lzma/CPP/Windows/StdAfx.h new file mode 100644 index 0000000..e7924c8 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/StdAfx.h @@ -0,0 +1,9 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../Common/MyWindows.h" +#include "../Common/NewHandler.h" + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Synchronization.cpp b/3rdparty/physfs/lzma/CPP/Windows/Synchronization.cpp new file mode 100644 index 0000000..5f86d1e --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Synchronization.cpp @@ -0,0 +1,10 @@ +// Windows/Synchronization.cpp + +#include "StdAfx.h" + +#include "Synchronization.h" + +namespace NWindows { +namespace NSynchronization { + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/Synchronization.h b/3rdparty/physfs/lzma/CPP/Windows/Synchronization.h new file mode 100644 index 0000000..c16f7b4 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Synchronization.h @@ -0,0 +1,168 @@ +// Windows/Synchronization.h + +#ifndef __WINDOWS_SYNCHRONIZATION_H +#define __WINDOWS_SYNCHRONIZATION_H + +#include "Defs.h" + +extern "C" +{ +#include "../../C/Threads.h" +} + +#ifdef _WIN32 +#include "Handle.h" +#endif + +namespace NWindows { +namespace NSynchronization { + +class CBaseEvent +{ +protected: + ::CEvent _object; +public: + bool IsCreated() { return Event_IsCreated(&_object) != 0; } + operator HANDLE() { return _object.handle; } + CBaseEvent() { Event_Construct(&_object); } + ~CBaseEvent() { Close(); } + HRes Close() { return Event_Close(&_object); } + #ifdef _WIN32 + HRes Create(bool manualReset, bool initiallyOwn, LPCTSTR name = NULL, + LPSECURITY_ATTRIBUTES securityAttributes = NULL) + { + _object.handle = ::CreateEvent(securityAttributes, BoolToBOOL(manualReset), + BoolToBOOL(initiallyOwn), name); + if (_object.handle != 0) + return 0; + return ::GetLastError(); + } + HRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) + { + _object.handle = ::OpenEvent(desiredAccess, BoolToBOOL(inheritHandle), name); + if (_object.handle != 0) + return 0; + return ::GetLastError(); + } + #endif + + HRes Set() { return Event_Set(&_object); } + // bool Pulse() { return BOOLToBool(::PulseEvent(_handle)); } + HRes Reset() { return Event_Reset(&_object); } + HRes Lock() { return Event_Wait(&_object); } +}; + +class CManualResetEvent: public CBaseEvent +{ +public: + HRes Create(bool initiallyOwn = false) + { + return ManualResetEvent_Create(&_object, initiallyOwn ? 1: 0); + } + HRes CreateIfNotCreated() + { + if (IsCreated()) + return 0; + return ManualResetEvent_CreateNotSignaled(&_object); + } + #ifdef _WIN32 + HRes CreateWithName(bool initiallyOwn, LPCTSTR name) + { + return CBaseEvent::Create(true, initiallyOwn, name); + } + #endif +}; + +class CAutoResetEvent: public CBaseEvent +{ +public: + HRes Create() + { + return AutoResetEvent_CreateNotSignaled(&_object); + } + HRes CreateIfNotCreated() + { + if (IsCreated()) + return 0; + return AutoResetEvent_CreateNotSignaled(&_object); + } +}; + +#ifdef _WIN32 +class CObject: public CHandle +{ +public: + HRes Lock(DWORD timeoutInterval = INFINITE) + { return (::WaitForSingleObject(_handle, timeoutInterval) == WAIT_OBJECT_0 ? 0 : ::GetLastError()); } +}; +class CMutex: public CObject +{ +public: + HRes Create(bool initiallyOwn, LPCTSTR name = NULL, + LPSECURITY_ATTRIBUTES securityAttributes = NULL) + { + _handle = ::CreateMutex(securityAttributes, BoolToBOOL(initiallyOwn), name); + if (_handle != 0) + return 0; + return ::GetLastError(); + } + HRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) + { + _handle = ::OpenMutex(desiredAccess, BoolToBOOL(inheritHandle), name); + if (_handle != 0) + return 0; + return ::GetLastError(); + } + HRes Release() + { + return ::ReleaseMutex(_handle) ? 0 : ::GetLastError(); + } +}; +class CMutexLock +{ + CMutex *_object; +public: + CMutexLock(CMutex &object): _object(&object) { _object->Lock(); } + ~CMutexLock() { _object->Release(); } +}; +#endif + +class CSemaphore +{ + ::CSemaphore _object; +public: + CSemaphore() { Semaphore_Construct(&_object); } + ~CSemaphore() { Close(); } + HRes Close() { return Semaphore_Close(&_object); } + operator HANDLE() { return _object.handle; } + HRes Create(UInt32 initiallyCount, UInt32 maxCount) + { + return Semaphore_Create(&_object, initiallyCount, maxCount); + } + HRes Release() { return Semaphore_Release1(&_object); } + HRes Release(UInt32 releaseCount) { return Semaphore_ReleaseN(&_object, releaseCount); } + HRes Lock() { return Semaphore_Wait(&_object); } +}; + +class CCriticalSection +{ + ::CCriticalSection _object; +public: + CCriticalSection() { CriticalSection_Init(&_object); } + ~CCriticalSection() { CriticalSection_Delete(&_object); } + void Enter() { CriticalSection_Enter(&_object); } + void Leave() { CriticalSection_Leave(&_object); } +}; + +class CCriticalSectionLock +{ + CCriticalSection *_object; + void Unlock() { _object->Leave(); } +public: + CCriticalSectionLock(CCriticalSection &object): _object(&object) {_object->Enter(); } + ~CCriticalSectionLock() { Unlock(); } +}; + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/System.cpp b/3rdparty/physfs/lzma/CPP/Windows/System.cpp new file mode 100644 index 0000000..8e4069c --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/System.cpp @@ -0,0 +1,64 @@ +// Windows/System.cpp + +#include "StdAfx.h" + +#include "System.h" + +namespace NWindows { +namespace NSystem { + +UInt32 GetNumberOfProcessors() +{ + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + return (UInt32)systemInfo.dwNumberOfProcessors; +} + +#if !defined(_WIN64) && defined(__GNUC__) + +typedef struct _MY_MEMORYSTATUSEX { + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; +} MY_MEMORYSTATUSEX, *MY_LPMEMORYSTATUSEX; + +#else + +#define MY_MEMORYSTATUSEX MEMORYSTATUSEX +#define MY_LPMEMORYSTATUSEX LPMEMORYSTATUSEX + +#endif + +typedef BOOL (WINAPI *GlobalMemoryStatusExP)(MY_LPMEMORYSTATUSEX lpBuffer); + +UInt64 GetRamSize() +{ + MY_MEMORYSTATUSEX stat; + stat.dwLength = sizeof(stat); + #ifdef _WIN64 + if (!::GlobalMemoryStatusEx(&stat)) + return 0; + return stat.ullTotalPhys; + #else + GlobalMemoryStatusExP globalMemoryStatusEx = (GlobalMemoryStatusExP) + ::GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")), + "GlobalMemoryStatusEx"); + if (globalMemoryStatusEx != 0) + if (globalMemoryStatusEx(&stat)) + return stat.ullTotalPhys; + { + MEMORYSTATUS stat; + stat.dwLength = sizeof(stat); + GlobalMemoryStatus(&stat); + return stat.dwTotalPhys; + } + #endif +} + +}} diff --git a/3rdparty/physfs/lzma/CPP/Windows/System.h b/3rdparty/physfs/lzma/CPP/Windows/System.h new file mode 100644 index 0000000..e006715 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/System.h @@ -0,0 +1,16 @@ +// Windows/System.h + +#ifndef __WINDOWS_SYSTEM_H +#define __WINDOWS_SYSTEM_H + +#include "../Common/Types.h" + +namespace NWindows { +namespace NSystem { + +UInt32 GetNumberOfProcessors(); +UInt64 GetRamSize(); + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Thread.h b/3rdparty/physfs/lzma/CPP/Windows/Thread.h new file mode 100644 index 0000000..a46a568 --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Thread.h @@ -0,0 +1,38 @@ +// Windows/Thread.h + +#ifndef __WINDOWS_THREAD_H +#define __WINDOWS_THREAD_H + +#include "Defs.h" + +extern "C" +{ +#include "../../C/Threads.h" +} + +namespace NWindows { + +class CThread +{ + ::CThread thread; +public: + CThread() { Thread_Construct(&thread); } + ~CThread() { Close(); } + bool IsCreated() { return Thread_WasCreated(&thread) != 0; } + HRes Close() { return Thread_Close(&thread); } + HRes Create(THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) + { return Thread_Create(&thread, startAddress, parameter); } + HRes Wait() { return Thread_Wait(&thread); } + + #ifdef _WIN32 + DWORD Resume() { return ::ResumeThread(thread.handle); } + DWORD Suspend() { return ::SuspendThread(thread.handle); } + bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread.handle, exitCode)); } + int GetPriority() { return ::GetThreadPriority(thread.handle); } + bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread.handle, priority)); } + #endif +}; + +} + +#endif diff --git a/3rdparty/physfs/lzma/CPP/Windows/Time.h b/3rdparty/physfs/lzma/CPP/Windows/Time.h new file mode 100644 index 0000000..fbba2dd --- /dev/null +++ b/3rdparty/physfs/lzma/CPP/Windows/Time.h @@ -0,0 +1,66 @@ +// Windows/Time.h + +#ifndef __WINDOWS_TIME_H +#define __WINDOWS_TIME_H + +#include "Common/Types.h" +#include "Windows/Defs.h" + +namespace NWindows { +namespace NTime { + +inline bool DosTimeToFileTime(UInt32 dosTime, FILETIME &fileTime) +{ + return BOOLToBool(::DosDateTimeToFileTime(UInt16(dosTime >> 16), + UInt16(dosTime & 0xFFFF), &fileTime)); +} + +const UInt32 kHighDosTime = 0xFF9FBF7D; +const UInt32 kLowDosTime = 0x210000; + +inline bool FileTimeToDosTime(const FILETIME &fileTime, UInt32 &dosTime) +{ + WORD datePart, timePart; + if (!::FileTimeToDosDateTime(&fileTime, &datePart, &timePart)) + { + if (fileTime.dwHighDateTime >= 0x01C00000) // 2000 + dosTime = kHighDosTime; + else + dosTime = kLowDosTime; + return false; + } + dosTime = (((UInt32)datePart) << 16) + timePart; + return true; +} + +const UInt32 kNumTimeQuantumsInSecond = 10000000; +const UInt64 kUnixTimeStartValue = ((UInt64)kNumTimeQuantumsInSecond) * 60 * 60 * 24 * 134774; + +inline void UnixTimeToFileTime(UInt32 unixTime, FILETIME &fileTime) +{ + UInt64 v = kUnixTimeStartValue + ((UInt64)unixTime) * kNumTimeQuantumsInSecond; + fileTime.dwLowDateTime = (DWORD)v; + fileTime.dwHighDateTime = (DWORD)(v >> 32); +} + +inline bool FileTimeToUnixTime(const FILETIME &fileTime, UInt32 &unixTime) +{ + UInt64 winTime = (((UInt64)fileTime.dwHighDateTime) << 32) + fileTime.dwLowDateTime; + if (winTime < kUnixTimeStartValue) + { + unixTime = 0; + return false; + } + winTime = (winTime - kUnixTimeStartValue) / kNumTimeQuantumsInSecond; + if (winTime > 0xFFFFFFFF) + { + unixTime = 0xFFFFFFFF; + return false; + } + unixTime = (UInt32)winTime; + return true; +} + +}} + +#endif diff --git a/3rdparty/physfs/lzma/CS/7zip/Common/CRC.cs b/3rdparty/physfs/lzma/CS/7zip/Common/CRC.cs new file mode 100644 index 0000000..82cc857 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Common/CRC.cs @@ -0,0 +1,55 @@ +// Common/CRC.cs + +namespace SevenZip +{ + class CRC + { + public static readonly uint[] Table; + + static CRC() + { + Table = new uint[256]; + const uint kPoly = 0xEDB88320; + for (uint i = 0; i < 256; i++) + { + uint r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >> 1) ^ kPoly; + else + r >>= 1; + Table[i] = r; + } + } + + uint _value = 0xFFFFFFFF; + + public void Init() { _value = 0xFFFFFFFF; } + + public void UpdateByte(byte b) + { + _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8); + } + + public void Update(byte[] data, uint offset, uint size) + { + for (uint i = 0; i < size; i++) + _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8); + } + + public uint GetDigest() { return _value ^ 0xFFFFFFFF; } + + static uint CalculateDigest(byte[] data, uint offset, uint size) + { + CRC crc = new CRC(); + // crc.Init(); + crc.Update(data, offset, size); + return crc.GetDigest(); + } + + static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) + { + return (CalculateDigest(data, offset, size) == digest); + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Common/CommandLineParser.cs b/3rdparty/physfs/lzma/CS/7zip/Common/CommandLineParser.cs new file mode 100644 index 0000000..8eabf59 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Common/CommandLineParser.cs @@ -0,0 +1,274 @@ +// CommandLineParser.cs + +using System; +using System.Collections; + +namespace SevenZip.CommandLineParser +{ + public enum SwitchType + { + Simple, + PostMinus, + LimitedPostString, + UnLimitedPostString, + PostChar + } + + public class SwitchForm + { + public string IDString; + public SwitchType Type; + public bool Multi; + public int MinLen; + public int MaxLen; + public string PostCharSet; + + public SwitchForm(string idString, SwitchType type, bool multi, + int minLen, int maxLen, string postCharSet) + { + IDString = idString; + Type = type; + Multi = multi; + MinLen = minLen; + MaxLen = maxLen; + PostCharSet = postCharSet; + } + public SwitchForm(string idString, SwitchType type, bool multi, int minLen): + this(idString, type, multi, minLen, 0, "") + { + } + public SwitchForm(string idString, SwitchType type, bool multi): + this(idString, type, multi, 0) + { + } + } + + public class SwitchResult + { + public bool ThereIs; + public bool WithMinus; + public ArrayList PostStrings = new ArrayList(); + public int PostCharIndex; + public SwitchResult() + { + ThereIs = false; + } + } + + public class Parser + { + public ArrayList NonSwitchStrings = new ArrayList(); + SwitchResult[] _switches; + + public Parser(int numSwitches) + { + _switches = new SwitchResult[numSwitches]; + for (int i = 0; i < numSwitches; i++) + _switches[i] = new SwitchResult(); + } + + bool ParseString(string srcString, SwitchForm[] switchForms) + { + int len = srcString.Length; + if (len == 0) + return false; + int pos = 0; + if (!IsItSwitchChar(srcString[pos])) + return false; + while (pos < len) + { + if (IsItSwitchChar(srcString[pos])) + pos++; + const int kNoLen = -1; + int matchedSwitchIndex = 0; + int maxLen = kNoLen; + for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++) + { + int switchLen = switchForms[switchIndex].IDString.Length; + if (switchLen <= maxLen || pos + switchLen > len) + continue; + if (String.Compare(switchForms[switchIndex].IDString, 0, + srcString, pos, switchLen, true) == 0) + { + matchedSwitchIndex = switchIndex; + maxLen = switchLen; + } + } + if (maxLen == kNoLen) + throw new Exception("maxLen == kNoLen"); + SwitchResult matchedSwitch = _switches[matchedSwitchIndex]; + SwitchForm switchForm = switchForms[matchedSwitchIndex]; + if ((!switchForm.Multi) && matchedSwitch.ThereIs) + throw new Exception("switch must be single"); + matchedSwitch.ThereIs = true; + pos += maxLen; + int tailSize = len - pos; + SwitchType type = switchForm.Type; + switch (type) + { + case SwitchType.PostMinus: + { + if (tailSize == 0) + matchedSwitch.WithMinus = false; + else + { + matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus); + if (matchedSwitch.WithMinus) + pos++; + } + break; + } + case SwitchType.PostChar: + { + if (tailSize < switchForm.MinLen) + throw new Exception("switch is not full"); + string charSet = switchForm.PostCharSet; + const int kEmptyCharValue = -1; + if (tailSize == 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + int index = charSet.IndexOf(srcString[pos]); + if (index < 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + matchedSwitch.PostCharIndex = index; + pos++; + } + } + break; + } + case SwitchType.LimitedPostString: + case SwitchType.UnLimitedPostString: + { + int minLen = switchForm.MinLen; + if (tailSize < minLen) + throw new Exception("switch is not full"); + if (type == SwitchType.UnLimitedPostString) + { + matchedSwitch.PostStrings.Add(srcString.Substring(pos)); + return true; + } + String stringSwitch = srcString.Substring(pos, minLen); + pos += minLen; + for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++) + { + char c = srcString[pos]; + if (IsItSwitchChar(c)) + break; + stringSwitch += c; + } + matchedSwitch.PostStrings.Add(stringSwitch); + break; + } + } + } + return true; + + } + + public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings) + { + int numCommandStrings = commandStrings.Length; + bool stopSwitch = false; + for (int i = 0; i < numCommandStrings; i++) + { + string s = commandStrings[i]; + if (stopSwitch) + NonSwitchStrings.Add(s); + else + if (s == kStopSwitchParsing) + stopSwitch = true; + else + if (!ParseString(s, switchForms)) + NonSwitchStrings.Add(s); + } + } + + public SwitchResult this[int index] { get { return _switches[index]; } } + + public static int ParseCommand(CommandForm[] commandForms, string commandString, + out string postString) + { + for (int i = 0; i < commandForms.Length; i++) + { + string id = commandForms[i].IDString; + if (commandForms[i].PostStringMode) + { + if (commandString.IndexOf(id) == 0) + { + postString = commandString.Substring(id.Length); + return i; + } + } + else + if (commandString == id) + { + postString = ""; + return i; + } + } + postString = ""; + return -1; + } + + static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, + string commandString, ArrayList indices) + { + indices.Clear(); + int numUsedChars = 0; + for (int i = 0; i < numForms; i++) + { + CommandSubCharsSet charsSet = forms[i]; + int currentIndex = -1; + int len = charsSet.Chars.Length; + for (int j = 0; j < len; j++) + { + char c = charsSet.Chars[j]; + int newIndex = commandString.IndexOf(c); + if (newIndex >= 0) + { + if (currentIndex >= 0) + return false; + if (commandString.IndexOf(c, newIndex + 1) >= 0) + return false; + currentIndex = j; + numUsedChars++; + } + } + if (currentIndex == -1 && !charsSet.EmptyAllowed) + return false; + indices.Add(currentIndex); + } + return (numUsedChars == commandString.Length); + } + const char kSwitchID1 = '-'; + const char kSwitchID2 = '/'; + + const char kSwitchMinus = '-'; + const string kStopSwitchParsing = "--"; + + static bool IsItSwitchChar(char c) + { + return (c == kSwitchID1 || c == kSwitchID2); + } + } + + public class CommandForm + { + public string IDString = ""; + public bool PostStringMode = false; + public CommandForm(string idString, bool postStringMode) + { + IDString = idString; + PostStringMode = postStringMode; + } + } + + class CommandSubCharsSet + { + public string Chars = ""; + public bool EmptyAllowed = false; + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Common/InBuffer.cs b/3rdparty/physfs/lzma/CS/7zip/Common/InBuffer.cs new file mode 100644 index 0000000..7c51f0b --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Common/InBuffer.cs @@ -0,0 +1,72 @@ +// InBuffer.cs + +namespace SevenZip.Buffer +{ + public class InBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_Limit; + uint m_BufferSize; + System.IO.Stream m_Stream; + bool m_StreamWasExhausted; + ulong m_ProcessedSize; + + public InBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void Init(System.IO.Stream stream) + { + m_Stream = stream; + m_ProcessedSize = 0; + m_Limit = 0; + m_Pos = 0; + m_StreamWasExhausted = false; + } + + public bool ReadBlock() + { + if (m_StreamWasExhausted) + return false; + m_ProcessedSize += m_Pos; + int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize); + m_Pos = 0; + m_Limit = (uint)aNumProcessedBytes; + m_StreamWasExhausted = (aNumProcessedBytes == 0); + return (!m_StreamWasExhausted); + } + + + public void ReleaseStream() + { + // m_Stream.Close(); + m_Stream = null; + } + + public bool ReadByte(byte b) // check it + { + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return false; + b = m_Buffer[m_Pos++]; + return true; + } + + public byte ReadByte() + { + // return (byte)m_Stream.ReadByte(); + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return 0xFF; + return m_Buffer[m_Pos++]; + } + + public ulong GetProcessedSize() + { + return m_ProcessedSize + m_Pos; + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Common/OutBuffer.cs b/3rdparty/physfs/lzma/CS/7zip/Common/OutBuffer.cs new file mode 100644 index 0000000..2da16e1 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Common/OutBuffer.cs @@ -0,0 +1,47 @@ +// OutBuffer.cs + +namespace SevenZip.Buffer +{ + public class OutBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_BufferSize; + System.IO.Stream m_Stream; + ulong m_ProcessedSize; + + public OutBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void SetStream(System.IO.Stream stream) { m_Stream = stream; } + public void FlushStream() { m_Stream.Flush(); } + public void CloseStream() { m_Stream.Close(); } + public void ReleaseStream() { m_Stream = null; } + + public void Init() + { + m_ProcessedSize = 0; + m_Pos = 0; + } + + public void WriteByte(byte b) + { + m_Buffer[m_Pos++] = b; + if (m_Pos >= m_BufferSize) + FlushData(); + } + + public void FlushData() + { + if (m_Pos == 0) + return; + m_Stream.Write(m_Buffer, 0, (int)m_Pos); + m_Pos = 0; + } + + public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs new file mode 100644 index 0000000..10ca2b3 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs @@ -0,0 +1,24 @@ +// IMatchFinder.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + interface IInWindowStream + { + void SetStream(System.IO.Stream inStream); + void Init(); + void ReleaseStream(); + Byte GetIndexByte(Int32 index); + UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit); + UInt32 GetNumAvailableBytes(); + } + + interface IMatchFinder : IInWindowStream + { + void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter); + UInt32 GetMatches(UInt32[] distances); + void Skip(UInt32 num); + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzBinTree.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzBinTree.cs new file mode 100644 index 0000000..c1c006b --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzBinTree.cs @@ -0,0 +1,367 @@ +// LzBinTree.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class BinTree : InWindow, IMatchFinder + { + UInt32 _cyclicBufferPos; + UInt32 _cyclicBufferSize = 0; + UInt32 _matchMaxLen; + + UInt32[] _son; + UInt32[] _hash; + + UInt32 _cutValue = 0xFF; + UInt32 _hashMask; + UInt32 _hashSizeSum = 0; + + bool HASH_ARRAY = true; + + const UInt32 kHash2Size = 1 << 10; + const UInt32 kHash3Size = 1 << 16; + const UInt32 kBT2HashSize = 1 << 16; + const UInt32 kStartMaxLen = 1; + const UInt32 kHash3Offset = kHash2Size; + const UInt32 kEmptyHashValue = 0; + const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1; + + UInt32 kNumHashDirectBytes = 0; + UInt32 kMinMatchCheck = 4; + UInt32 kFixHashSize = kHash2Size + kHash3Size; + + public void SetType(int numHashBytes) + { + HASH_ARRAY = (numHashBytes > 2); + if (HASH_ARRAY) + { + kNumHashDirectBytes = 0; + kMinMatchCheck = 4; + kFixHashSize = kHash2Size + kHash3Size; + } + else + { + kNumHashDirectBytes = 2; + kMinMatchCheck = 2 + 1; + kFixHashSize = 0; + } + } + + public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); } + public new void ReleaseStream() { base.ReleaseStream(); } + + public new void Init() + { + base.Init(); + for (UInt32 i = 0; i < _hashSizeSum; i++) + _hash[i] = kEmptyHashValue; + _cyclicBufferPos = 0; + ReduceOffsets(-1); + } + + public new void MovePos() + { + if (++_cyclicBufferPos >= _cyclicBufferSize) + _cyclicBufferPos = 0; + base.MovePos(); + if (_pos == kMaxValForNormalize) + Normalize(); + } + + public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); } + + public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { return base.GetMatchLen(index, distance, limit); } + + public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); } + + public void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter) + { + if (historySize > kMaxValForNormalize - 256) + throw new Exception(); + _cutValue = 16 + (matchMaxLen >> 1); + + UInt32 windowReservSize = (historySize + keepAddBufferBefore + + matchMaxLen + keepAddBufferAfter) / 2 + 256; + + base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize); + + _matchMaxLen = matchMaxLen; + + UInt32 cyclicBufferSize = historySize + 1; + if (_cyclicBufferSize != cyclicBufferSize) + _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2]; + + UInt32 hs = kBT2HashSize; + + if (HASH_ARRAY) + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) + hs >>= 1; + _hashMask = hs; + hs++; + hs += kFixHashSize; + } + if (hs != _hashSizeSum) + _hash = new UInt32[_hashSizeSum = hs]; + } + + public UInt32 GetMatches(UInt32[] distances) + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + return 0; + } + } + + UInt32 offset = 0; + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize; + UInt32 hashValue, hash2Value = 0, hash3Value = 0; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + hash2Value = temp & (kHash2Size - 1); + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + hash3Value = temp & (kHash3Size - 1); + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + if (HASH_ARRAY) + { + UInt32 curMatch2 = _hash[hash2Value]; + UInt32 curMatch3 = _hash[kHash3Offset + hash3Value]; + _hash[hash2Value] = _pos; + _hash[kHash3Offset + hash3Value] = _pos; + if (curMatch2 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) + { + distances[offset++] = maxLen = 2; + distances[offset++] = _pos - curMatch2 - 1; + } + if (curMatch3 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) + { + if (curMatch3 == curMatch2) + offset -= 2; + distances[offset++] = maxLen = 3; + distances[offset++] = _pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + if (offset != 0 && curMatch2 == curMatch) + { + offset -= 2; + maxLen = kStartMaxLen; + } + } + + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + if (kNumHashDirectBytes != 0) + { + if (curMatch > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] != + _bufferBase[cur + kNumHashDirectBytes]) + { + distances[offset++] = maxLen = kNumHashDirectBytes; + distances[offset++] = _pos - curMatch - 1; + } + } + } + + UInt32 count = _cutValue; + + while(true) + { + if(curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while(++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (maxLen < len) + { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + return offset; + } + + public void Skip(UInt32 num) + { + do + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + continue; + } + } + + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + + UInt32 hashValue; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + UInt32 hash2Value = temp & (kHash2Size - 1); + _hash[hash2Value] = _pos; + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + UInt32 hash3Value = temp & (kHash3Size - 1); + _hash[kHash3Offset + hash3Value] = _pos; + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + UInt32 count = _cutValue; + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while (++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + } + while (--num != 0); + } + + void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue) + { + for (UInt32 i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } + } + + void Normalize() + { + UInt32 subValue = _pos - _cyclicBufferSize; + NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); + NormalizeLinks(_hash, _hashSizeSum, subValue); + ReduceOffsets((Int32)subValue); + } + + public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzInWindow.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzInWindow.cs new file mode 100644 index 0000000..52d23ce --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzInWindow.cs @@ -0,0 +1,132 @@ +// LzInWindow.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class InWindow + { + public Byte[] _bufferBase = null; // pointer to buffer with data + System.IO.Stream _stream; + UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done + bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream + + UInt32 _pointerToLastSafePosition; + + public UInt32 _bufferOffset; + + public UInt32 _blockSize; // Size of Allocated memory block + public UInt32 _pos; // offset (from _buffer) of curent byte + UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos + UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos + public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream + + public void MoveBlock() + { + UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore; + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) + offset--; + + UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset; + + // check negative offset ???? + for (UInt32 i = 0; i < numBytes; i++) + _bufferBase[i] = _bufferBase[offset + i]; + _bufferOffset -= offset; + } + + public virtual void ReadBlock() + { + if (_streamEndWasReached) + return; + while (true) + { + int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos); + if (size == 0) + return; + int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size); + if (numReadBytes == 0) + { + _posLimit = _streamPos; + UInt32 pointerToPostion = _bufferOffset + _posLimit; + if (pointerToPostion > _pointerToLastSafePosition) + _posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset); + + _streamEndWasReached = true; + return; + } + _streamPos += (UInt32)numReadBytes; + if (_streamPos >= _pos + _keepSizeAfter) + _posLimit = _streamPos - _keepSizeAfter; + } + } + + void Free() { _bufferBase = null; } + + public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv) + { + _keepSizeBefore = keepSizeBefore; + _keepSizeAfter = keepSizeAfter; + UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; + if (_bufferBase == null || _blockSize != blockSize) + { + Free(); + _blockSize = blockSize; + _bufferBase = new Byte[_blockSize]; + } + _pointerToLastSafePosition = _blockSize - keepSizeAfter; + } + + public void SetStream(System.IO.Stream stream) { _stream = stream; } + public void ReleaseStream() { _stream = null; } + + public void Init() + { + _bufferOffset = 0; + _pos = 0; + _streamPos = 0; + _streamEndWasReached = false; + ReadBlock(); + } + + public void MovePos() + { + _pos++; + if (_pos > _posLimit) + { + UInt32 pointerToPostion = _bufferOffset + _pos; + if (pointerToPostion > _pointerToLastSafePosition) + MoveBlock(); + ReadBlock(); + } + } + + public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; } + + // index + limit have not to exceed _keepSizeAfter; + public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { + if (_streamEndWasReached) + if ((_pos + index) + limit > _streamPos) + limit = _streamPos - (UInt32)(_pos + index); + distance++; + // Byte *pby = _buffer + (size_t)_pos + index; + UInt32 pby = _bufferOffset + _pos + (UInt32)index; + + UInt32 i; + for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++); + return i; + } + + public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; } + + public void ReduceOffsets(Int32 subValue) + { + _bufferOffset += (UInt32)subValue; + _posLimit -= (UInt32)subValue; + _pos -= (UInt32)subValue; + _streamPos -= (UInt32)subValue; + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs new file mode 100644 index 0000000..c998584 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs @@ -0,0 +1,110 @@ +// LzOutWindow.cs + +namespace SevenZip.Compression.LZ +{ + public class OutWindow + { + byte[] _buffer = null; + uint _pos; + uint _windowSize = 0; + uint _streamPos; + System.IO.Stream _stream; + + public uint TrainSize = 0; + + public void Create(uint windowSize) + { + if (_windowSize != windowSize) + { + // System.GC.Collect(); + _buffer = new byte[windowSize]; + } + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + } + + public void Init(System.IO.Stream stream, bool solid) + { + ReleaseStream(); + _stream = stream; + if (!solid) + { + _streamPos = 0; + _pos = 0; + TrainSize = 0; + } + } + + public bool Train(System.IO.Stream stream) + { + long len = stream.Length; + uint size = (len < _windowSize) ? (uint)len : _windowSize; + TrainSize = size; + stream.Position = len - size; + _streamPos = _pos = 0; + while (size > 0) + { + uint curSize = _windowSize - _pos; + if (size < curSize) + curSize = size; + int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize); + if (numReadBytes == 0) + return false; + size -= (uint)numReadBytes; + _pos += (uint)numReadBytes; + _streamPos += (uint)numReadBytes; + if (_pos == _windowSize) + _streamPos = _pos = 0; + } + return true; + } + + public void ReleaseStream() + { + Flush(); + _stream = null; + } + + public void Flush() + { + uint size = _pos - _streamPos; + if (size == 0) + return; + _stream.Write(_buffer, (int)_streamPos, (int)size); + if (_pos >= _windowSize) + _pos = 0; + _streamPos = _pos; + } + + public void CopyBlock(uint distance, uint len) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + for (; len > 0; len--) + { + if (pos >= _windowSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos >= _windowSize) + Flush(); + } + } + + public void PutByte(byte b) + { + _buffer[_pos++] = b; + if (_pos >= _windowSize) + Flush(); + } + + public byte GetByte(uint distance) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + return _buffer[pos]; + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs new file mode 100644 index 0000000..c7bca86 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs @@ -0,0 +1,76 @@ +// LzmaBase.cs + +namespace SevenZip.Compression.LZMA +{ + internal abstract class Base + { + public const uint kNumRepDistances = 4; + public const uint kNumStates = 12; + + // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; + // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; + // static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; + // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + + public struct State + { + public uint Index; + public void Init() { Index = 0; } + public void UpdateChar() + { + if (Index < 4) Index = 0; + else if (Index < 10) Index -= 3; + else Index -= 6; + } + public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); } + public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); } + public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); } + public bool IsCharState() { return Index < 7; } + } + + public const int kNumPosSlotBits = 6; + public const int kDicLogSizeMin = 0; + // public const int kDicLogSizeMax = 30; + // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; + + public const int kNumLenToPosStatesBits = 2; // it's for speed optimization + public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits; + + public const uint kMatchMinLen = 2; + + public static uint GetLenToPosState(uint len) + { + len -= kMatchMinLen; + if (len < kNumLenToPosStates) + return len; + return (uint)(kNumLenToPosStates - 1); + } + + public const int kNumAlignBits = 4; + public const uint kAlignTableSize = 1 << kNumAlignBits; + public const uint kAlignMask = (kAlignTableSize - 1); + + public const uint kStartPosModelIndex = 4; + public const uint kEndPosModelIndex = 14; + public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + + public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2); + + public const uint kNumLitPosStatesBitsEncodingMax = 4; + public const uint kNumLitContextBitsMax = 8; + + public const int kNumPosStatesBitsMax = 4; + public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + public const int kNumPosStatesBitsEncodingMax = 4; + public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + public const int kNumLowLenBits = 3; + public const int kNumMidLenBits = 3; + public const int kNumHighLenBits = 8; + public const uint kNumLowLenSymbols = 1 << kNumLowLenBits; + public const uint kNumMidLenSymbols = 1 << kNumMidLenBits; + public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + + (1 << kNumHighLenBits); + public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1; + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs new file mode 100644 index 0000000..a9be39f --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs @@ -0,0 +1,398 @@ +// LzmaDecoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream + { + class LenDecoder + { + BitDecoder m_Choice = new BitDecoder(); + BitDecoder m_Choice2 = new BitDecoder(); + BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); + uint m_NumPosStates = 0; + + public void Create(uint numPosStates) + { + for (uint posState = m_NumPosStates; posState < numPosStates; posState++) + { + m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); + m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); + } + m_NumPosStates = numPosStates; + } + + public void Init() + { + m_Choice.Init(); + for (uint posState = 0; posState < m_NumPosStates; posState++) + { + m_LowCoder[posState].Init(); + m_MidCoder[posState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) + { + if (m_Choice.Decode(rangeDecoder) == 0) + return m_LowCoder[posState].Decode(rangeDecoder); + else + { + uint symbol = Base.kNumLowLenSymbols; + if (m_Choice2.Decode(rangeDecoder) == 0) + symbol += m_MidCoder[posState].Decode(rangeDecoder); + else + { + symbol += Base.kNumMidLenSymbols; + symbol += m_HighCoder.Decode(rangeDecoder); + } + return symbol; + } + } + } + + class LiteralDecoder + { + struct Decoder2 + { + BitDecoder[] m_Decoders; + public void Create() { m_Decoders = new BitDecoder[0x300]; } + public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) + { + uint symbol = 1; + do + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + while (symbol < 0x100); + return (byte)symbol; + } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) + { + uint symbol = 1; + do + { + uint matchBit = (uint)(matchByte >> 7) & 1; + matchByte <<= 1; + uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + break; + } + } + while (symbol < 0x100); + return (byte)symbol; + } + } + + Decoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && + m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Decoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + uint GetState(uint pos, byte prevByte) + { return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) + { return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) + { return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } + }; + + LZ.OutWindow m_OutWindow = new LZ.OutWindow(); + RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder(); + + BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; + BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + + BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + + LenDecoder m_LenDecoder = new LenDecoder(); + LenDecoder m_RepLenDecoder = new LenDecoder(); + + LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + + uint m_DictionarySize; + uint m_DictionarySizeCheck; + + uint m_PosStateMask; + + public Decoder() + { + m_DictionarySize = 0xFFFFFFFF; + for (int i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + } + + void SetDictionarySize(uint dictionarySize) + { + if (m_DictionarySize != dictionarySize) + { + m_DictionarySize = dictionarySize; + m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1); + uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12)); + m_OutWindow.Create(blockSize); + } + } + + void SetLiteralProperties(int lp, int lc) + { + if (lp > 8) + throw new InvalidParamException(); + if (lc > 8) + throw new InvalidParamException(); + m_LiteralDecoder.Create(lp, lc); + } + + void SetPosBitsProperties(int pb) + { + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + uint numPosStates = (uint)1 << pb; + m_LenDecoder.Create(numPosStates); + m_RepLenDecoder.Create(numPosStates); + m_PosStateMask = numPosStates - 1; + } + + bool _solid = false; + void Init(System.IO.Stream inStream, System.IO.Stream outStream) + { + m_RangeDecoder.Init(inStream); + m_OutWindow.Init(outStream, _solid); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= m_PosStateMask; j++) + { + uint index = (i << Base.kNumPosStatesBitsMax) + j; + m_IsMatchDecoders[index].Init(); + m_IsRep0LongDecoders[index].Init(); + } + m_IsRepDecoders[i].Init(); + m_IsRepG0Decoders[i].Init(); + m_IsRepG1Decoders[i].Init(); + m_IsRepG2Decoders[i].Init(); + } + + m_LiteralDecoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + // m_PosSpecDecoder.Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepLenDecoder.Init(); + m_PosAlignDecoder.Init(); + } + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + Init(inStream, outStream); + + Base.State state = new Base.State(); + state.Init(); + uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0; + + UInt64 nowPos64 = 0; + UInt64 outSize64 = (UInt64)outSize; + if (nowPos64 < outSize64) + { + if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0) + throw new DataErrorException(); + state.UpdateChar(); + byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0); + m_OutWindow.PutByte(b); + nowPos64++; + } + while (nowPos64 < outSize64) + { + // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64); + // while(nowPos64 < next) + { + uint posState = (uint)nowPos64 & m_PosStateMask; + if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + byte b; + byte prevByte = m_OutWindow.GetByte(0); + if (!state.IsCharState()) + b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, + (uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0)); + else + b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte); + m_OutWindow.PutByte(b); + state.UpdateChar(); + nowPos64++; + } + else + { + uint len; + if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1) + { + if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + state.UpdateShortRep(); + m_OutWindow.PutByte(m_OutWindow.GetByte(rep0)); + nowPos64++; + continue; + } + } + else + { + UInt32 distance; + if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + distance = rep1; + } + else + { + if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen; + state.UpdateRep(); + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState); + state.UpdateMatch(); + uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder); + if (posSlot >= Base.kStartPosModelIndex) + { + int numDirectBits = (int)((posSlot >> 1) - 1); + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.kEndPosModelIndex) + rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 - posSlot - 1, m_RangeDecoder, numDirectBits); + else + { + rep0 += (m_RangeDecoder.DecodeDirectBits( + numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); + rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); + } + } + else + rep0 = posSlot; + } + if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck) + { + if (rep0 == 0xFFFFFFFF) + break; + throw new DataErrorException(); + } + m_OutWindow.CopyBlock(rep0, len); + nowPos64 += len; + } + } + } + m_OutWindow.Flush(); + m_OutWindow.ReleaseStream(); + m_RangeDecoder.ReleaseStream(); + } + + public void SetDecoderProperties(byte[] properties) + { + if (properties.Length < 5) + throw new InvalidParamException(); + int lc = properties[0] % 9; + int remainder = properties[0] / 9; + int lp = remainder % 5; + int pb = remainder / 5; + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + UInt32 dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); + SetDictionarySize(dictionarySize); + SetLiteralProperties(lp, lc); + SetPosBitsProperties(pb); + } + + public bool Train(System.IO.Stream stream) + { + _solid = true; + return m_OutWindow.Train(stream); + } + + /* + public override bool CanRead { get { return true; }} + public override bool CanWrite { get { return true; }} + public override bool CanSeek { get { return true; }} + public override long Length { get { return 0; }} + public override long Position + { + get { return 0; } + set { } + } + public override void Flush() { } + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + public override void Write(byte[] buffer, int offset, int count) + { + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return 0; + } + public override void SetLength(long value) {} + */ + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs new file mode 100644 index 0000000..a8d6723 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs @@ -0,0 +1,1480 @@ +// LzmaEncoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + { + enum EMatchFinderType + { + BT2, + BT4, + }; + + const UInt32 kIfinityPrice = 0xFFFFFFF; + + static Byte[] g_FastPos = new Byte[1 << 11]; + + static Encoder() + { + const Byte kFastSlots = 22; + int c = 2; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) + { + UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1)); + for (UInt32 j = 0; j < k; j++, c++) + g_FastPos[c] = slotFast; + } + } + + static UInt32 GetPosSlot(UInt32 pos) + { + if (pos < (1 << 11)) + return g_FastPos[pos]; + if (pos < (1 << 21)) + return (UInt32)(g_FastPos[pos >> 10] + 20); + return (UInt32)(g_FastPos[pos >> 20] + 40); + } + + static UInt32 GetPosSlot2(UInt32 pos) + { + if (pos < (1 << 17)) + return (UInt32)(g_FastPos[pos >> 6] + 12); + if (pos < (1 << 27)) + return (UInt32)(g_FastPos[pos >> 16] + 32); + return (UInt32)(g_FastPos[pos >> 26] + 52); + } + + Base.State _state = new Base.State(); + Byte _previousByte; + UInt32[] _repDistances = new UInt32[Base.kNumRepDistances]; + + void BaseInit() + { + _state.Init(); + _previousByte = 0; + for (UInt32 i = 0; i < Base.kNumRepDistances; i++) + _repDistances[i] = 0; + } + + const int kDefaultDictionaryLogSize = 22; + const UInt32 kNumFastBytesDefault = 0x20; + + class LiteralEncoder + { + public struct Encoder2 + { + BitEncoder[] m_Encoders; + + public void Create() { m_Encoders = new BitEncoder[0x300]; } + + public void Init() { for (int i = 0; i < 0x300; i++) m_Encoders[i].Init(); } + + public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) + { + uint context = 1; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + m_Encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) + { + uint context = 1; + bool same = true; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + uint state = context; + if (same) + { + uint matchBit = (uint)((matchByte >> i) & 1); + state += ((1 + matchBit) << 8); + same = (matchBit == bit); + } + m_Encoders[state].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public uint GetPrice(bool matchMode, byte matchByte, byte symbol) + { + uint price = 0; + uint context = 1; + int i = 7; + if (matchMode) + { + for (; i >= 0; i--) + { + uint matchBit = (uint)(matchByte >> i) & 1; + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + context = (context << 1) | bit; + if (matchBit != bit) + { + i--; + break; + } + } + } + for (; i >= 0; i--) + { + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[context].GetPrice(bit); + context = (context << 1) | bit; + } + return price; + } + } + + Encoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Encoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) + { return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; } + } + + class LenEncoder + { + RangeCoder.BitEncoder _choice = new RangeCoder.BitEncoder(); + RangeCoder.BitEncoder _choice2 = new RangeCoder.BitEncoder(); + RangeCoder.BitTreeEncoder[] _lowCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder[] _midCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder _highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits); + + public LenEncoder() + { + for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) + { + _lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits); + _midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits); + } + } + + public void Init(UInt32 numPosStates) + { + _choice.Init(); + _choice2.Init(); + for (UInt32 posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + + public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + if (symbol < Base.kNumLowLenSymbols) + { + _choice.Encode(rangeEncoder, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + symbol -= Base.kNumLowLenSymbols; + _choice.Encode(rangeEncoder, 1); + if (symbol < Base.kNumMidLenSymbols) + { + _choice2.Encode(rangeEncoder, 0); + _midCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + _choice2.Encode(rangeEncoder, 1); + _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + } + } + } + + public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st) + { + UInt32 a0 = _choice.GetPrice0(); + UInt32 a1 = _choice.GetPrice1(); + UInt32 b0 = a1 + _choice2.GetPrice0(); + UInt32 b1 = a1 + _choice2.GetPrice1(); + UInt32 i = 0; + for (i = 0; i < Base.kNumLowLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols); + } + for (; i < numSymbols; i++) + prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + } + }; + + const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + + class LenPriceTableEncoder : LenEncoder + { + UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax]; + UInt32 _tableSize; + UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax]; + + public void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; } + + public UInt32 GetPrice(UInt32 symbol, UInt32 posState) + { + return _prices[posState * Base.kNumLenSymbols + symbol]; + } + + void UpdateTable(UInt32 posState) + { + SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols); + _counters[posState] = _tableSize; + } + + public void UpdateTables(UInt32 numPosStates) + { + for (UInt32 posState = 0; posState < numPosStates; posState++) + UpdateTable(posState); + } + + public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + base.Encode(rangeEncoder, symbol, posState); + if (--_counters[posState] == 0) + UpdateTable(posState); + } + } + + const UInt32 kNumOpts = 1 << 12; + class Optimal + { + public Base.State State; + + public bool Prev1IsChar; + public bool Prev2; + + public UInt32 PosPrev2; + public UInt32 BackPrev2; + + public UInt32 Price; + public UInt32 PosPrev; + public UInt32 BackPrev; + + public UInt32 Backs0; + public UInt32 Backs1; + public UInt32 Backs2; + public UInt32 Backs3; + + public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; } + public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; } + public bool IsShortRep() { return (BackPrev == 0); } + }; + Optimal[] _optimum = new Optimal[kNumOpts]; + LZ.IMatchFinder _matchFinder = null; + RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); + + RangeCoder.BitEncoder[] _isMatch = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + RangeCoder.BitEncoder[] _isRep = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG0 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG1 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG2 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRep0Long = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + RangeCoder.BitTreeEncoder[] _posSlotEncoder = new RangeCoder.BitTreeEncoder[Base.kNumLenToPosStates]; + + RangeCoder.BitEncoder[] _posEncoders = new RangeCoder.BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + RangeCoder.BitTreeEncoder _posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits); + + LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); + LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); + + LiteralEncoder _literalEncoder = new LiteralEncoder(); + + UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2]; + + UInt32 _numFastBytes = kNumFastBytesDefault; + UInt32 _longestMatchLength; + UInt32 _numDistancePairs; + + UInt32 _additionalOffset; + + UInt32 _optimumEndIndex; + UInt32 _optimumCurrentIndex; + + bool _longestMatchWasFound; + + UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)]; + UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits]; + UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize]; + UInt32 _alignPriceCount; + + UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2); + + int _posStateBits = 2; + UInt32 _posStateMask = (4 - 1); + int _numLiteralPosStateBits = 0; + int _numLiteralContextBits = 3; + + UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize); + UInt32 _dictionarySizePrev = 0xFFFFFFFF; + UInt32 _numFastBytesPrev = 0xFFFFFFFF; + + Int64 nowPos64; + bool _finished; + System.IO.Stream _inStream; + + EMatchFinderType _matchFinderType = EMatchFinderType.BT4; + bool _writeEndMark = false; + + bool _needReleaseMFStream; + + void Create() + { + if (_matchFinder == null) + { + LZ.BinTree bt = new LZ.BinTree(); + int numHashBytes = 4; + if (_matchFinderType == EMatchFinderType.BT2) + numHashBytes = 2; + bt.SetType(numHashBytes); + _matchFinder = bt; + } + _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); + + if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes) + return; + _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1); + _dictionarySizePrev = _dictionarySize; + _numFastBytesPrev = _numFastBytes; + } + + public Encoder() + { + for (int i = 0; i < kNumOpts; i++) + _optimum[i] = new Optimal(); + for (int i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits); + } + + void SetWriteEndMarkerMode(bool writeEndMarker) + { + _writeEndMark = writeEndMarker; + } + + void Init() + { + BaseInit(); + _rangeEncoder.Init(); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= _posStateMask; j++) + { + uint complexState = (i << Base.kNumPosStatesBitsMax) + j; + _isMatch[complexState].Init(); + _isRep0Long[complexState].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + _literalEncoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i].Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + _posEncoders[i].Init(); + + _lenEncoder.Init((UInt32)1 << _posStateBits); + _repMatchLenEncoder.Init((UInt32)1 << _posStateBits); + + _posAlignEncoder.Init(); + + _longestMatchWasFound = false; + _optimumEndIndex = 0; + _optimumCurrentIndex = 0; + _additionalOffset = 0; + } + + void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs) + { + lenRes = 0; + numDistancePairs = _matchFinder.GetMatches(_matchDistances); + if (numDistancePairs > 0) + { + lenRes = _matchDistances[numDistancePairs - 2]; + if (lenRes == _numFastBytes) + lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1], + Base.kMatchMaxLen - lenRes); + } + _additionalOffset++; + } + + + void MovePos(UInt32 num) + { + if (num > 0) + { + _matchFinder.Skip(num); + _additionalOffset += num; + } + } + + UInt32 GetRepLen1Price(Base.State state, UInt32 posState) + { + return _isRepG0[state.Index].GetPrice0() + + _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0(); + } + + UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState) + { + UInt32 price; + if (repIndex == 0) + { + price = _isRepG0[state.Index].GetPrice0(); + price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + } + else + { + price = _isRepG0[state.Index].GetPrice1(); + if (repIndex == 1) + price += _isRepG1[state.Index].GetPrice0(); + else + { + price += _isRepG1[state.Index].GetPrice1(); + price += _isRepG2[state.Index].GetPrice(repIndex - 2); + } + } + return price; + } + + UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState) + { + UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + return price + GetPureRepPrice(repIndex, state, posState); + } + + UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState) + { + UInt32 price; + UInt32 lenToPosState = Base.GetLenToPosState(len); + if (pos < Base.kNumFullDistances) + price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + else + price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.kAlignMask]; + return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + } + + UInt32 Backward(out UInt32 backRes, UInt32 cur) + { + _optimumEndIndex = cur; + UInt32 posMem = _optimum[cur].PosPrev; + UInt32 backMem = _optimum[cur].BackPrev; + do + { + if (_optimum[cur].Prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem].PosPrev = posMem - 1; + if (_optimum[cur].Prev2) + { + _optimum[posMem - 1].Prev1IsChar = false; + _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; + _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + } + } + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = _optimum[posPrev].BackPrev; + posMem = _optimum[posPrev].PosPrev; + + _optimum[posPrev].BackPrev = backCur; + _optimum[posPrev].PosPrev = cur; + cur = posPrev; + } + while (cur > 0); + backRes = _optimum[0].BackPrev; + _optimumCurrentIndex = _optimum[0].PosPrev; + return _optimumCurrentIndex; + } + + UInt32[] reps = new UInt32[Base.kNumRepDistances]; + UInt32[] repLens = new UInt32[Base.kNumRepDistances]; + + + UInt32 GetOptimum(UInt32 position, out UInt32 backRes) + { + if (_optimumEndIndex != _optimumCurrentIndex) + { + UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex].BackPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + UInt32 lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + ReadMatchDistances(out lenMain, out numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; + if (numAvailableBytes < 2) + { + backRes = 0xFFFFFFFF; + return 1; + } + if (numAvailableBytes > Base.kMatchMaxLen) + numAvailableBytes = Base.kMatchMaxLen; + + UInt32 repMaxIndex = 0; + UInt32 i; + for (i = 0; i < Base.kNumRepDistances; i++) + { + reps[i] = _repDistances[i]; + repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen); + if (repLens[i] > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + UInt32 lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + if (lenMain >= _numFastBytes) + { + backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + + Byte currentByte = _matchFinder.GetIndexByte(0 - 1); + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1)); + + if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + { + backRes = (UInt32)0xFFFFFFFF; + return 1; + } + + _optimum[0].State = _state; + + UInt32 posState = (position & _posStateMask); + + _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1(); + + if (matchByte == currentByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if (shortRepPrice < _optimum[1].Price) + { + _optimum[1].Price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + + UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + + if(lenEnd < 2) + { + backRes = _optimum[1].BackPrev; + return 1; + } + + _optimum[1].PosPrev = 0; + + _optimum[0].Backs0 = reps[0]; + _optimum[0].Backs1 = reps[1]; + _optimum[0].Backs2 = reps[2]; + _optimum[0].Backs3 = reps[3]; + + UInt32 len = lenEnd; + do + _optimum[len--].Price = kIfinityPrice; + while (len >= 2); + + for (i = 0; i < Base.kNumRepDistances; i++) + { + UInt32 repLen = repLens[i]; + if (repLen < 2) + continue; + UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + Optimal optimum = _optimum[repLen]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = i; + optimum.Prev1IsChar = false; + } + } + while (--repLen >= 2); + } + + UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0(); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= lenMain) + { + UInt32 offs = 0; + while (len > _matchDistances[offs]) + offs += 2; + for (; ; len++) + { + UInt32 distance = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); + Optimal optimum = _optimum[len]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = distance + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + if (len == _matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + + UInt32 cur = 0; + + while (true) + { + cur++; + if (cur == lenEnd) + return Backward(out backRes, cur); + UInt32 newLen; + ReadMatchDistances(out newLen, out numDistancePairs); + if (newLen >= _numFastBytes) + { + _numDistancePairs = numDistancePairs; + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(out backRes, cur); + } + position++; + UInt32 posPrev = _optimum[cur].PosPrev; + Base.State state; + if (_optimum[cur].Prev1IsChar) + { + posPrev--; + if (_optimum[cur].Prev2) + { + state = _optimum[_optimum[cur].PosPrev2].State; + if (_optimum[cur].BackPrev2 < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + else + state = _optimum[posPrev].State; + state.UpdateChar(); + } + else + state = _optimum[posPrev].State; + if (posPrev == cur - 1) + { + if (_optimum[cur].IsShortRep()) + state.UpdateShortRep(); + else + state.UpdateChar(); + } + else + { + UInt32 pos; + if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2) + { + posPrev = _optimum[cur].PosPrev2; + pos = _optimum[cur].BackPrev2; + state.UpdateRep(); + } + else + { + pos = _optimum[cur].BackPrev; + if (pos < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + Optimal opt = _optimum[posPrev]; + if (pos < Base.kNumRepDistances) + { + if (pos == 0) + { + reps[0] = opt.Backs0; + reps[1] = opt.Backs1; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 1) + { + reps[0] = opt.Backs1; + reps[1] = opt.Backs0; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 2) + { + reps[0] = opt.Backs2; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs3; + } + else + { + reps[0] = opt.Backs3; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + else + { + reps[0] = (pos - Base.kNumRepDistances); + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + _optimum[cur].State = state; + _optimum[cur].Backs0 = reps[0]; + _optimum[cur].Backs1 = reps[1]; + _optimum[cur].Backs2 = reps[2]; + _optimum[cur].Backs3 = reps[3]; + UInt32 curPrice = _optimum[cur].Price; + + currentByte = _matchFinder.GetIndexByte(0 - 1); + matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1)); + + posState = (position & _posStateMask); + + UInt32 curAnd1Price = curPrice + + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)). + GetPrice(!state.IsCharState(), matchByte, currentByte); + + Optimal nextOptimum = _optimum[cur + 1]; + + bool nextIsChar = false; + if (curAnd1Price < nextOptimum.Price) + { + nextOptimum.Price = curAnd1Price; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1(); + + if (matchByte == currentByte && + !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if (shortRepPrice <= nextOptimum.Price) + { + nextOptimum.Price = shortRepPrice; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsShortRep(); + nextIsChar = true; + } + } + + UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; + numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) + continue; + if (numAvailableBytes > _numFastBytes) + numAvailableBytes = _numFastBytes; + if (!nextIsChar && matchByte != currentByte) + { + // try Literal + rep0 + UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateChar(); + UInt32 posStateNext = (position + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAnd1Price + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + { + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = false; + } + } + } + } + + UInt32 startLen = 2; // speed optimization + + for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) + { + UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes); + if (lenTest < 2) + continue; + UInt32 lenTestTemp = lenTest; + do + { + while (lenEnd < cur + lenTest) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = repIndex; + optimum.Prev1IsChar = false; + } + } + while(--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateRep(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = + repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).GetPrice(true, + _matchFinder.GetIndexByte((Int32)((Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1))), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = lenTest + 1 + lenTest2; + while(lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = repIndex; + } + } + } + } + } + + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ; + _matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0(); + while (lenEnd < cur + newLen) + _optimum[++lenEnd].Price = kIfinityPrice; + + UInt32 offs = 0; + while (startLen > _matchDistances[offs]) + offs += 2; + + for (UInt32 lenTest = startLen; ; lenTest++) + { + UInt32 curBack = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = curBack + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + + if (lenTest == _matchDistances[offs]) + { + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, curBack, t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateMatch(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)). + GetPrice(true, + _matchFinder.GetIndexByte((Int32)lenTest - (Int32)(curBack + 1) - 1), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + UInt32 offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = curBack + Base.kNumRepDistances; + } + } + } + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + } + } + + bool ChangePair(UInt32 smallDist, UInt32 bigDist) + { + const int kDif = 7; + return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif)); + } + + void WriteEndMarker(UInt32 posState) + { + if (!_writeEndMark) + return; + + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1); + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + UInt32 len = Base.kMatchMinLen; + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1; + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + int footerBits = 30; + UInt32 posReduced = (((UInt32)1) << footerBits) - 1; + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + } + + void Flush(UInt32 nowPos) + { + ReleaseMFStream(); + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + _rangeEncoder.FlushStream(); + } + + public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished) + { + inSize = 0; + outSize = 0; + finished = true; + + if (_inStream != null) + { + _matchFinder.SetStream(_inStream); + _matchFinder.Init(); + _needReleaseMFStream = true; + _inStream = null; + if (_trainSize > 0) + _matchFinder.Skip(_trainSize); + } + + if (_finished) + return; + _finished = true; + + + Int64 progressPosValuePrev = nowPos64; + if (nowPos64 == 0) + { + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + UInt32 len, numDistancePairs; // it's not used + ReadMatchDistances(out len, out numDistancePairs); + UInt32 posState = (UInt32)(nowPos64) & _posStateMask; + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0); + _state.UpdateChar(); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + _literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + nowPos64++; + } + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + while (true) + { + UInt32 pos; + UInt32 len = GetOptimum((UInt32)nowPos64, out pos); + + UInt32 posState = ((UInt32)nowPos64) & _posStateMask; + UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState; + if (len == 1 && pos == 0xFFFFFFFF) + { + _isMatch[complexState].Encode(_rangeEncoder, 0); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte); + if (!_state.IsCharState()) + { + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset)); + subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); + } + else + subCoder.Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _state.UpdateChar(); + } + else + { + _isMatch[complexState].Encode(_rangeEncoder, 1); + if (pos < Base.kNumRepDistances) + { + _isRep[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 0) + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 0); + if (len == 1) + _isRep0Long[complexState].Encode(_rangeEncoder, 0); + else + _isRep0Long[complexState].Encode(_rangeEncoder, 1); + } + else + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 1) + _isRepG1[_state.Index].Encode(_rangeEncoder, 0); + else + { + _isRepG1[_state.Index].Encode(_rangeEncoder, 1); + _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2); + } + } + if (len == 1) + _state.UpdateShortRep(); + else + { + _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + _state.UpdateRep(); + } + UInt32 distance = _repDistances[pos]; + if (pos != 0) + { + for (UInt32 i = pos; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + } + } + else + { + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + pos -= Base.kNumRepDistances; + UInt32 posSlot = GetPosSlot(pos); + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + + if (posSlot >= Base.kStartPosModelIndex) + { + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - baseVal; + + if (posSlot < Base.kEndPosModelIndex) + RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders, + baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced); + else + { + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _alignPriceCount++; + } + } + UInt32 distance = pos; + for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset)); + } + _additionalOffset -= len; + nowPos64 += len; + if (_additionalOffset == 0) + { + // if (!_fastMode) + if (_matchPriceCount >= (1 << 7)) + FillDistancesPrices(); + if (_alignPriceCount >= Base.kAlignTableSize) + FillAlignPrices(); + inSize = nowPos64; + outSize = _rangeEncoder.GetProcessedSizeAdd(); + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + + if (nowPos64 - progressPosValuePrev >= (1 << 12)) + { + _finished = false; + finished = false; + return; + } + } + } + } + + void ReleaseMFStream() + { + if (_matchFinder != null && _needReleaseMFStream) + { + _matchFinder.ReleaseStream(); + _needReleaseMFStream = false; + } + } + + void SetOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); } + void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); } + + void ReleaseStreams() + { + ReleaseMFStream(); + ReleaseOutStream(); + } + + void SetStreams(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize) + { + _inStream = inStream; + _finished = false; + Create(); + SetOutStream(outStream); + Init(); + + // if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _lenEncoder.UpdateTables((UInt32)1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits); + + nowPos64 = 0; + } + + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + _needReleaseMFStream = false; + try + { + SetStreams(inStream, outStream, inSize, outSize); + while (true) + { + Int64 processedInSize; + Int64 processedOutSize; + bool finished; + CodeOneBlock(out processedInSize, out processedOutSize, out finished); + if (finished) + return; + if (progress != null) + { + progress.SetProgress(processedInSize, processedOutSize); + } + } + } + finally + { + ReleaseStreams(); + } + } + + const int kPropSize = 5; + Byte[] properties = new Byte[kPropSize]; + + public void WriteCoderProperties(System.IO.Stream outStream) + { + properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + for (int i = 0; i < 4; i++) + properties[1 + i] = (Byte)(_dictionarySize >> (8 * i)); + outStream.Write(properties, 0, kPropSize); + } + + UInt32[] tempPrices = new UInt32[Base.kNumFullDistances]; + UInt32 _matchPriceCount; + + void FillDistancesPrices() + { + for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot(i); + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, + baseVal - posSlot - 1, footerBits, i - baseVal); + } + + for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + RangeCoder.BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; + + UInt32 st = (lenToPosState << Base.kNumPosSlotBits); + for (posSlot = 0; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); + for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits); + + UInt32 st2 = lenToPosState * Base.kNumFullDistances; + UInt32 i; + for (i = 0; i < Base.kStartPosModelIndex; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + i]; + for (; i < Base.kNumFullDistances; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i]; + } + _matchPriceCount = 0; + } + + void FillAlignPrices() + { + for (UInt32 i = 0; i < Base.kAlignTableSize; i++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _alignPriceCount = 0; + } + + + static string[] kMatchFinderIDs = + { + "BT2", + "BT4", + }; + + static int FindMatchFinder(string s) + { + for (int m = 0; m < kMatchFinderIDs.Length; m++) + if (s == kMatchFinderIDs[m]) + return m; + return -1; + } + + public void SetCoderProperties(CoderPropID[] propIDs, object[] properties) + { + for (UInt32 i = 0; i < properties.Length; i++) + { + object prop = properties[i]; + switch (propIDs[i]) + { + case CoderPropID.NumFastBytes: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 numFastBytes = (Int32)prop; + if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) + throw new InvalidParamException(); + _numFastBytes = (UInt32)numFastBytes; + break; + } + case CoderPropID.Algorithm: + { + /* + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 maximize = (Int32)prop; + _fastMode = (maximize == 0); + _maxMode = (maximize >= 2); + */ + break; + } + case CoderPropID.MatchFinder: + { + if (!(prop is String)) + throw new InvalidParamException(); + EMatchFinderType matchFinderIndexPrev = _matchFinderType; + int m = FindMatchFinder(((string)prop).ToUpper()); + if (m < 0) + throw new InvalidParamException(); + _matchFinderType = (EMatchFinderType)m; + if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) + { + _dictionarySizePrev = 0xFFFFFFFF; + _matchFinder = null; + } + break; + } + case CoderPropID.DictionarySize: + { + const int kDicLogSizeMaxCompress = 30; + if (!(prop is Int32)) + throw new InvalidParamException(); ; + Int32 dictionarySize = (Int32)prop; + if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) || + dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress)) + throw new InvalidParamException(); + _dictionarySize = (UInt32)dictionarySize; + int dicLogSize; + for (dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++) + if (dictionarySize <= ((UInt32)(1) << dicLogSize)) + break; + _distTableSize = (UInt32)dicLogSize * 2; + break; + } + case CoderPropID.PosStateBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _posStateBits = (int)v; + _posStateMask = (((UInt32)1) << (int)_posStateBits) - 1; + break; + } + case CoderPropID.LitPosBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _numLiteralPosStateBits = (int)v; + break; + } + case CoderPropID.LitContextBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitContextBitsMax) + throw new InvalidParamException(); ; + _numLiteralContextBits = (int)v; + break; + } + case CoderPropID.EndMarker: + { + if (!(prop is Boolean)) + throw new InvalidParamException(); + SetWriteEndMarkerMode((Boolean)prop); + break; + } + default: + throw new InvalidParamException(); + } + } + } + + uint _trainSize = 0; + public void SetTrainSize(uint trainSize) + { + _trainSize = trainSize; + } + + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs new file mode 100644 index 0000000..38018a2 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs @@ -0,0 +1,364 @@ +using System; +using System.IO; +namespace SevenZip +{ + using CommandLineParser; + + public class CDoubleStream: Stream + { + public System.IO.Stream s1; + public System.IO.Stream s2; + public int fileIndex; + public long skipSize; + + public override bool CanRead { get { return true; }} + public override bool CanWrite { get { return false; }} + public override bool CanSeek { get { return false; }} + public override long Length { get { return s1.Length + s2.Length - skipSize; } } + public override long Position + { + get { return 0; } + set { } + } + public override void Flush() { } + public override int Read(byte[] buffer, int offset, int count) + { + int numTotal = 0; + while (count > 0) + { + if (fileIndex == 0) + { + int num = s1.Read(buffer, offset, count); + offset += num; + count -= num; + numTotal += num; + if (num == 0) + fileIndex++; + } + if (fileIndex == 1) + { + numTotal += s2.Read(buffer, offset, count); + return numTotal; + } + } + return numTotal; + } + public override void Write(byte[] buffer, int offset, int count) + { + throw (new Exception("can't Write")); + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw (new Exception("can't Seek")); + } + public override void SetLength(long value) + { + throw (new Exception("can't SetLength")); + } + } + + class LzmaAlone + { + enum Key + { + Help1 = 0, + Help2, + Mode, + Dictionary, + FastBytes, + LitContext, + LitPos, + PosBits, + MatchFinder, + EOS, + StdIn, + StdOut, + Train + }; + + static void PrintHelp() + { + System.Console.WriteLine("\nUsage: LZMA [...] inputFile outputFile\n" + + " e: encode file\n" + + " d: decode file\n" + + " b: Benchmark\n" + + "\n" + + // " -a{N}: set compression mode - [0, 1], default: 1 (max)\n" + + " -d{N}: set dictionary - [0, 29], default: 23 (8MB)\n" + + " -fb{N}: set number of fast bytes - [5, 273], default: 128\n" + + " -lc{N}: set number of literal context bits - [0, 8], default: 3\n" + + " -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" + + " -pb{N}: set number of pos bits - [0, 4], default: 2\n" + + " -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" + + " -eos: write End Of Stream marker\n" + // + " -si: read data from stdin\n" + // + " -so: write data to stdout\n" + ); + } + + static bool GetNumber(string s, out Int32 v) + { + v = 0; + for (int i = 0; i < s.Length; i++) + { + char c = s[i]; + if (c < '0' || c > '9') + return false; + v *= 10; + v += (Int32)(c - '0'); + } + return true; + } + + static int IncorrectCommand() + { + throw (new Exception("Command line error")); + // System.Console.WriteLine("\nCommand line error\n"); + // return 1; + } + static int Main2(string[] args) + { + System.Console.WriteLine("\nLZMA# 4.49 Copyright (c) 1999-2007 Igor Pavlov 2006-07-05\n"); + + if (args.Length == 0) + { + PrintHelp(); + return 0; + } + + SwitchForm[] kSwitchForms = new SwitchForm[13]; + int sw = 0; + kSwitchForms[sw++] = new SwitchForm("?", SwitchType.Simple, false); + kSwitchForms[sw++] = new SwitchForm("H", SwitchType.Simple, false); + kSwitchForms[sw++] = new SwitchForm("A", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("D", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("FB", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("LC", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("LP", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("PB", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("MF", SwitchType.UnLimitedPostString, false, 1); + kSwitchForms[sw++] = new SwitchForm("EOS", SwitchType.Simple, false); + kSwitchForms[sw++] = new SwitchForm("SI", SwitchType.Simple, false); + kSwitchForms[sw++] = new SwitchForm("SO", SwitchType.Simple, false); + kSwitchForms[sw++] = new SwitchForm("T", SwitchType.UnLimitedPostString, false, 1); + + + Parser parser = new Parser(sw); + try + { + parser.ParseStrings(kSwitchForms, args); + } + catch + { + return IncorrectCommand(); + } + + if (parser[(int)Key.Help1].ThereIs || parser[(int)Key.Help2].ThereIs) + { + PrintHelp(); + return 0; + } + + System.Collections.ArrayList nonSwitchStrings = parser.NonSwitchStrings; + + int paramIndex = 0; + if (paramIndex >= nonSwitchStrings.Count) + return IncorrectCommand(); + string command = (string)nonSwitchStrings[paramIndex++]; + command = command.ToLower(); + + bool dictionaryIsDefined = false; + Int32 dictionary = 1 << 21; + if (parser[(int)Key.Dictionary].ThereIs) + { + Int32 dicLog; + if (!GetNumber((string)parser[(int)Key.Dictionary].PostStrings[0], out dicLog)) + IncorrectCommand(); + dictionary = (Int32)1 << dicLog; + dictionaryIsDefined = true; + } + string mf = "bt4"; + if (parser[(int)Key.MatchFinder].ThereIs) + mf = (string)parser[(int)Key.MatchFinder].PostStrings[0]; + mf = mf.ToLower(); + + if (command == "b") + { + const Int32 kNumDefaultItereations = 10; + Int32 numIterations = kNumDefaultItereations; + if (paramIndex < nonSwitchStrings.Count) + if (!GetNumber((string)nonSwitchStrings[paramIndex++], out numIterations)) + numIterations = kNumDefaultItereations; + return LzmaBench.LzmaBenchmark(numIterations, (UInt32)dictionary); + } + + string train = ""; + if (parser[(int)Key.Train].ThereIs) + train = (string)parser[(int)Key.Train].PostStrings[0]; + + bool encodeMode = false; + if (command == "e") + encodeMode = true; + else if (command == "d") + encodeMode = false; + else + IncorrectCommand(); + + bool stdInMode = parser[(int)Key.StdIn].ThereIs; + bool stdOutMode = parser[(int)Key.StdOut].ThereIs; + + Stream inStream = null; + if (stdInMode) + { + throw (new Exception("Not implemeted")); + } + else + { + if (paramIndex >= nonSwitchStrings.Count) + IncorrectCommand(); + string inputName = (string)nonSwitchStrings[paramIndex++]; + inStream = new FileStream(inputName, FileMode.Open, FileAccess.Read); + } + + FileStream outStream = null; + if (stdOutMode) + { + throw (new Exception("Not implemeted")); + } + else + { + if (paramIndex >= nonSwitchStrings.Count) + IncorrectCommand(); + string outputName = (string)nonSwitchStrings[paramIndex++]; + outStream = new FileStream(outputName, FileMode.Create, FileAccess.Write); + } + + FileStream trainStream = null; + if (train.Length != 0) + trainStream = new FileStream(train, FileMode.Open, FileAccess.Read); + + if (encodeMode) + { + if (!dictionaryIsDefined) + dictionary = 1 << 23; + + Int32 posStateBits = 2; + Int32 litContextBits = 3; // for normal files + // UInt32 litContextBits = 0; // for 32-bit data + Int32 litPosBits = 0; + // UInt32 litPosBits = 2; // for 32-bit data + Int32 algorithm = 2; + Int32 numFastBytes = 128; + + bool eos = parser[(int)Key.EOS].ThereIs || stdInMode; + + if (parser[(int)Key.Mode].ThereIs) + if (!GetNumber((string)parser[(int)Key.Mode].PostStrings[0], out algorithm)) + IncorrectCommand(); + + if (parser[(int)Key.FastBytes].ThereIs) + if (!GetNumber((string)parser[(int)Key.FastBytes].PostStrings[0], out numFastBytes)) + IncorrectCommand(); + if (parser[(int)Key.LitContext].ThereIs) + if (!GetNumber((string)parser[(int)Key.LitContext].PostStrings[0], out litContextBits)) + IncorrectCommand(); + if (parser[(int)Key.LitPos].ThereIs) + if (!GetNumber((string)parser[(int)Key.LitPos].PostStrings[0], out litPosBits)) + IncorrectCommand(); + if (parser[(int)Key.PosBits].ThereIs) + if (!GetNumber((string)parser[(int)Key.PosBits].PostStrings[0], out posStateBits)) + IncorrectCommand(); + + CoderPropID[] propIDs = + { + CoderPropID.DictionarySize, + CoderPropID.PosStateBits, + CoderPropID.LitContextBits, + CoderPropID.LitPosBits, + CoderPropID.Algorithm, + CoderPropID.NumFastBytes, + CoderPropID.MatchFinder, + CoderPropID.EndMarker + }; + object[] properties = + { + (Int32)(dictionary), + (Int32)(posStateBits), + (Int32)(litContextBits), + (Int32)(litPosBits), + (Int32)(algorithm), + (Int32)(numFastBytes), + mf, + eos + }; + + Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder(); + encoder.SetCoderProperties(propIDs, properties); + encoder.WriteCoderProperties(outStream); + Int64 fileSize; + if (eos || stdInMode) + fileSize = -1; + else + fileSize = inStream.Length; + for (int i = 0; i < 8; i++) + outStream.WriteByte((Byte)(fileSize >> (8 * i))); + if (trainStream != null) + { + CDoubleStream doubleStream = new CDoubleStream(); + doubleStream.s1 = trainStream; + doubleStream.s2 = inStream; + doubleStream.fileIndex = 0; + inStream = doubleStream; + long trainFileSize = trainStream.Length; + doubleStream.skipSize = 0; + if (trainFileSize > dictionary) + doubleStream.skipSize = trainFileSize - dictionary; + trainStream.Seek(doubleStream.skipSize, SeekOrigin.Begin); + encoder.SetTrainSize((uint)(trainFileSize - doubleStream.skipSize)); + } + encoder.Code(inStream, outStream, -1, -1, null); + } + else if (command == "d") + { + byte[] properties = new byte[5]; + if (inStream.Read(properties, 0, 5) != 5) + throw (new Exception("input .lzma is too short")); + Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder(); + decoder.SetDecoderProperties(properties); + if (trainStream != null) + { + if (!decoder.Train(trainStream)) + throw (new Exception("can't train")); + } + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = inStream.ReadByte(); + if (v < 0) + throw (new Exception("Can't Read 1")); + outSize |= ((long)(byte)v) << (8 * i); + } + long compressedSize = inStream.Length - inStream.Position; + decoder.Code(inStream, outStream, compressedSize, outSize, null); + } + else + throw (new Exception("Command Error")); + return 0; + } + + [STAThread] + static int Main(string[] args) + { + try + { + return Main2(args); + } + catch (Exception e) + { + Console.WriteLine("{0} Caught exception #1.", e); + // throw e; + return 1; + } + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj new file mode 100644 index 0000000..6d87b61 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj @@ -0,0 +1,90 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {CE33DF18-F9C8-4D6F-9057-DBB4DB96E973} + Exe + LzmaAlone + Lzma# + 4 + + + true + full + false + .\bin\Debug\ + DEBUG;TRACE + + + false + true + .\bin\Release\ + TRACE + AnyCPU + + + + + + + + + Common\CommandLineParser.cs + + + Common\CRC.cs + + + ICoder.cs + + + LZ\IMatchFinder.cs + + + LZ\LzBinTree.cs + + + LZ\LzInWindow.cs + + + LZ\LzOutWindow.cs + + + LZMA\LzmaBase.cs + + + LZMA\LzmaDecoder.cs + + + LZMA\LzmaEncoder.cs + + + RangeCoder\RangeCoder.cs + + + RangeCoder\RangeCoderBit.cs + + + RangeCoder\RangeCoderBitTree.cs + + + Code + + + Code + + + + True + Settings.settings + + + SettingsSingleFileGenerator + Settings.cs + + + + + \ No newline at end of file diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln new file mode 100644 index 0000000..376cd27 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C# Express 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LzmaAlone", "LzmaAlone.csproj", "{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs new file mode 100644 index 0000000..f7b6bd0 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs @@ -0,0 +1,340 @@ +// LzmaBench.cs + +using System; +using System.IO; + +namespace SevenZip +{ + /// + /// LZMA Benchmark + /// + internal abstract class LzmaBench + { + const UInt32 kAdditionalSize = (6 << 20); + const UInt32 kCompressedAdditionalSize = (1 << 10); + const UInt32 kMaxLzmaPropSize = 10; + + class CRandomGenerator + { + UInt32 A1; + UInt32 A2; + public CRandomGenerator() { Init(); } + public void Init() { A1 = 362436069; A2 = 521288629; } + public UInt32 GetRnd() + { + return + ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^ + ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16))); + } + }; + + class CBitRandomGenerator + { + CRandomGenerator RG = new CRandomGenerator(); + UInt32 Value; + int NumBits; + public void Init() + { + Value = 0; + NumBits = 0; + } + public UInt32 GetRnd(int numBits) + { + UInt32 result; + if (NumBits > numBits) + { + result = Value & (((UInt32)1 << numBits) - 1); + Value >>= numBits; + NumBits -= numBits; + return result; + } + numBits -= NumBits; + result = (Value << numBits); + Value = RG.GetRnd(); + result |= Value & (((UInt32)1 << numBits) - 1); + Value >>= numBits; + NumBits = 32 - numBits; + return result; + } + }; + + class CBenchRandomGenerator + { + CBitRandomGenerator RG = new CBitRandomGenerator(); + UInt32 Pos; + UInt32 Rep0; + + public UInt32 BufferSize; + public Byte[] Buffer = null; + + public CBenchRandomGenerator() { } + + public void Set(UInt32 bufferSize) + { + Buffer = new Byte[bufferSize]; + Pos = 0; + BufferSize = bufferSize; + } + UInt32 GetRndBit() { return RG.GetRnd(1); } + UInt32 GetLogRandBits(int numBits) + { + UInt32 len = RG.GetRnd(numBits); + return RG.GetRnd((int)len); + } + UInt32 GetOffset() + { + if (GetRndBit() == 0) + return GetLogRandBits(4); + return (GetLogRandBits(4) << 10) | RG.GetRnd(10); + } + UInt32 GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); } + UInt32 GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); } + public void Generate() + { + RG.Init(); + Rep0 = 1; + while (Pos < BufferSize) + { + if (GetRndBit() == 0 || Pos < 1) + Buffer[Pos++] = (Byte)RG.GetRnd(8); + else + { + UInt32 len; + if (RG.GetRnd(3) == 0) + len = 1 + GetLen1(); + else + { + do + Rep0 = GetOffset(); + while (Rep0 >= Pos); + Rep0++; + len = 2 + GetLen2(); + } + for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++) + Buffer[Pos] = Buffer[Pos - Rep0]; + } + } + } + }; + + class CrcOutStream : System.IO.Stream + { + public CRC CRC = new CRC(); + public void Init() { CRC.Init(); } + public UInt32 GetDigest() { return CRC.GetDigest(); } + + public override bool CanRead { get { return false; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return true; } } + public override Int64 Length { get { return 0; } } + public override Int64 Position { get { return 0; } set { } } + public override void Flush() { } + public override long Seek(long offset, SeekOrigin origin) { return 0; } + public override void SetLength(long value) { } + public override int Read(byte[] buffer, int offset, int count) { return 0; } + + public override void WriteByte(byte b) + { + CRC.UpdateByte(b); + } + public override void Write(byte[] buffer, int offset, int count) + { + CRC.Update(buffer, (uint)offset, (uint)count); + } + }; + + class CProgressInfo : ICodeProgress + { + public Int64 ApprovedStart; + public Int64 InSize; + public System.DateTime Time; + public void Init() { InSize = 0; } + public void SetProgress(Int64 inSize, Int64 outSize) + { + if (inSize >= ApprovedStart && InSize == 0) + { + Time = DateTime.UtcNow; + InSize = inSize; + } + } + } + const int kSubBits = 8; + + static UInt32 GetLogSize(UInt32 size) + { + for (int i = kSubBits; i < 32; i++) + for (UInt32 j = 0; j < (1 << kSubBits); j++) + if (size <= (((UInt32)1) << i) + (j << (i - kSubBits))) + return (UInt32)(i << kSubBits) + j; + return (32 << kSubBits); + } + + static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime) + { + UInt64 freq = TimeSpan.TicksPerSecond; + UInt64 elTime = elapsedTime; + while (freq > 1000000) + { + freq >>= 1; + elTime >>= 1; + } + if (elTime == 0) + elTime = 1; + return value * freq / elTime; + } + + static UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size) + { + UInt64 t = GetLogSize(dictionarySize) - (18 << kSubBits); + UInt64 numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits)); + UInt64 numCommands = (UInt64)(size) * numCommandsForOne; + return MyMultDiv64(numCommands, elapsedTime); + } + + static UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize) + { + UInt64 numCommands = inSize * 220 + outSize * 20; + return MyMultDiv64(numCommands, elapsedTime); + } + + static UInt64 GetTotalRating( + UInt32 dictionarySize, + UInt64 elapsedTimeEn, UInt64 sizeEn, + UInt64 elapsedTimeDe, + UInt64 inSizeDe, UInt64 outSizeDe) + { + return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) + + GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2; + } + + static void PrintValue(UInt64 v) + { + string s = v.ToString(); + for (int i = 0; i + s.Length < 6; i++) + System.Console.Write(" "); + System.Console.Write(s); + } + + static void PrintRating(UInt64 rating) + { + PrintValue(rating / 1000000); + System.Console.Write(" MIPS"); + } + + static void PrintResults( + UInt32 dictionarySize, + UInt64 elapsedTime, + UInt64 size, + bool decompressMode, UInt64 secondSize) + { + UInt64 speed = MyMultDiv64(size, elapsedTime); + PrintValue(speed / 1024); + System.Console.Write(" KB/s "); + UInt64 rating; + if (decompressMode) + rating = GetDecompressRating(elapsedTime, size, secondSize); + else + rating = GetCompressRating(dictionarySize, elapsedTime, size); + PrintRating(rating); + } + + static public int LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize) + { + if (numIterations <= 0) + return 0; + if (dictionarySize < (1 << 18)) + { + System.Console.WriteLine("\nError: dictionary size for benchmark must be >= 19 (512 KB)"); + return 1; + } + System.Console.Write("\n Compressing Decompressing\n\n"); + + Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder(); + Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder(); + + + CoderPropID[] propIDs = + { + CoderPropID.DictionarySize, + }; + object[] properties = + { + (Int32)(dictionarySize), + }; + + UInt32 kBufferSize = dictionarySize + kAdditionalSize; + UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize; + + encoder.SetCoderProperties(propIDs, properties); + System.IO.MemoryStream propStream = new System.IO.MemoryStream(); + encoder.WriteCoderProperties(propStream); + byte[] propArray = propStream.ToArray(); + + CBenchRandomGenerator rg = new CBenchRandomGenerator(); + + rg.Set(kBufferSize); + rg.Generate(); + CRC crc = new CRC(); + crc.Init(); + crc.Update(rg.Buffer, 0, rg.BufferSize); + + CProgressInfo progressInfo = new CProgressInfo(); + progressInfo.ApprovedStart = dictionarySize; + + UInt64 totalBenchSize = 0; + UInt64 totalEncodeTime = 0; + UInt64 totalDecodeTime = 0; + UInt64 totalCompressedSize = 0; + + MemoryStream inStream = new MemoryStream(rg.Buffer, 0, (int)rg.BufferSize); + MemoryStream compressedStream = new MemoryStream((int)kCompressedBufferSize); + CrcOutStream crcOutStream = new CrcOutStream(); + for (Int32 i = 0; i < numIterations; i++) + { + progressInfo.Init(); + inStream.Seek(0, SeekOrigin.Begin); + compressedStream.Seek(0, SeekOrigin.Begin); + encoder.Code(inStream, compressedStream, -1, -1, progressInfo); + TimeSpan sp2 = DateTime.UtcNow - progressInfo.Time; + UInt64 encodeTime = (UInt64)sp2.Ticks; + + long compressedSize = compressedStream.Position; + if (progressInfo.InSize == 0) + throw (new Exception("Internal ERROR 1282")); + + UInt64 decodeTime = 0; + for (int j = 0; j < 2; j++) + { + compressedStream.Seek(0, SeekOrigin.Begin); + crcOutStream.Init(); + + decoder.SetDecoderProperties(propArray); + UInt64 outSize = kBufferSize; + System.DateTime startTime = DateTime.UtcNow; + decoder.Code(compressedStream, crcOutStream, 0, (Int64)outSize, null); + TimeSpan sp = (DateTime.UtcNow - startTime); + decodeTime = (ulong)sp.Ticks; + if (crcOutStream.GetDigest() != crc.GetDigest()) + throw (new Exception("CRC Error")); + } + UInt64 benchSize = kBufferSize - (UInt64)progressInfo.InSize; + PrintResults(dictionarySize, encodeTime, benchSize, false, 0); + System.Console.Write(" "); + PrintResults(dictionarySize, decodeTime, kBufferSize, true, (ulong)compressedSize); + System.Console.WriteLine(); + + totalBenchSize += benchSize; + totalEncodeTime += encodeTime; + totalDecodeTime += decodeTime; + totalCompressedSize += (ulong)compressedSize; + } + System.Console.WriteLine("---------------------------------------------------"); + PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0); + System.Console.Write(" "); + PrintResults(dictionarySize, totalDecodeTime, + kBufferSize * (UInt64)numIterations, true, totalCompressedSize); + System.Console.WriteLine(" Average"); + return 0; + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9614884 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LZMA#")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Igor Pavlov")] +[assembly: AssemblyProduct("LZMA# SDK")] +[assembly: AssemblyCopyright("Copyright @ Igor Pavlov 1999-2004")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("4.12.*")] diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs new file mode 100644 index 0000000..1170cf1 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.40607.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LzmaAlone.Properties +{ + using System; + using System.IO; + using System.Resources; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the Strongly Typed Resource Builder + // class via a tool like ResGen or Visual Studio.NET. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + class Resources + { + + private static System.Resources.ResourceManager _resMgr; + + private static System.Globalization.CultureInfo _resCulture; + + /*FamANDAssem*/ + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Resources.ResourceManager ResourceManager + { + get + { + if ((_resMgr == null)) + { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Resources", typeof(Resources).Assembly); + _resMgr = temp; + } + return _resMgr; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Globalization.CultureInfo Culture + { + get + { + return _resCulture; + } + set + { + _resCulture = value; + } + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs new file mode 100644 index 0000000..ccfed77 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.40607.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LzmaAlone.Properties +{ + public partial class Settings : System.Configuration.ApplicationSettingsBase + { + private static Settings m_Value; + + private static object m_SyncObject = new object(); + + public static Settings Value + { + get + { + if ((Settings.m_Value == null)) + { + System.Threading.Monitor.Enter(Settings.m_SyncObject); + if ((Settings.m_Value == null)) + { + try + { + Settings.m_Value = new Settings(); + } + finally + { + System.Threading.Monitor.Exit(Settings.m_SyncObject); + } + } + } + return Settings.m_Value; + } + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs new file mode 100644 index 0000000..949c6bb --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs @@ -0,0 +1,234 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + class Encoder + { + public const uint kTopValue = (1 << 24); + + System.IO.Stream Stream; + + public UInt64 Low; + public uint Range; + uint _cacheSize; + byte _cache; + + long StartPosition; + + public void SetStream(System.IO.Stream stream) + { + Stream = stream; + } + + public void ReleaseStream() + { + Stream = null; + } + + public void Init() + { + StartPosition = Stream.Position; + + Low = 0; + Range = 0xFFFFFFFF; + _cacheSize = 1; + _cache = 0; + } + + public void FlushData() + { + for (int i = 0; i < 5; i++) + ShiftLow(); + } + + public void FlushStream() + { + Stream.Flush(); + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Encode(uint start, uint size, uint total) + { + Low += start * (Range /= total); + Range *= size; + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public void ShiftLow() + { + if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1) + { + byte temp = _cache; + do + { + Stream.WriteByte((byte)(temp + (Low >> 32))); + temp = 0xFF; + } + while (--_cacheSize != 0); + _cache = (byte)(((uint)Low) >> 24); + } + _cacheSize++; + Low = ((uint)Low) << 8; + } + + public void EncodeDirectBits(uint v, int numTotalBits) + { + for (int i = numTotalBits - 1; i >= 0; i--) + { + Range >>= 1; + if (((v >> i) & 1) == 1) + Low += Range; + if (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + } + + public void EncodeBit(uint size0, int numTotalBits, uint symbol) + { + uint newBound = (Range >> numTotalBits) * size0; + if (symbol == 0) + Range = newBound; + else + { + Low += newBound; + Range -= newBound; + } + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public long GetProcessedSizeAdd() + { + return _cacheSize + + Stream.Position - StartPosition + 4; + // (long)Stream.GetProcessedSize(); + } + } + + class Decoder + { + public const uint kTopValue = (1 << 24); + public uint Range; + public uint Code; + // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); + public System.IO.Stream Stream; + + public void Init(System.IO.Stream stream) + { + // Stream.Init(stream); + Stream = stream; + + Code = 0; + Range = 0xFFFFFFFF; + for (int i = 0; i < 5; i++) + Code = (Code << 8) | (byte)Stream.ReadByte(); + } + + public void ReleaseStream() + { + // Stream.ReleaseStream(); + Stream = null; + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Normalize() + { + while (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public void Normalize2() + { + if (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public uint GetThreshold(uint total) + { + return Code / (Range /= total); + } + + public void Decode(uint start, uint size, uint total) + { + Code -= start * Range; + Range *= size; + Normalize(); + } + + public uint DecodeDirectBits(int numTotalBits) + { + uint range = Range; + uint code = Code; + uint result = 0; + for (int i = numTotalBits; i > 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + uint t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < kTopValue) + { + code = (code << 8) | (byte)Stream.ReadByte(); + range <<= 8; + } + } + Range = range; + Code = code; + return result; + } + + public uint DecodeBit(uint size0, int numTotalBits) + { + uint newBound = (Range >> numTotalBits) * size0; + uint symbol; + if (Code < newBound) + { + symbol = 0; + Range = newBound; + } + else + { + symbol = 1; + Code -= newBound; + Range -= newBound; + } + Normalize(); + return symbol; + } + + // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs new file mode 100644 index 0000000..4f0346d --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs @@ -0,0 +1,117 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitEncoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + const int kNumMoveReducingBits = 2; + public const int kNumBitPriceShiftBits = 6; + + uint Prob; + + public void Init() { Prob = kBitModelTotal >> 1; } + + public void UpdateModel(uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + else + Prob -= (Prob) >> kNumMoveBits; + } + + public void Encode(Encoder encoder, uint symbol) + { + // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); + // UpdateModel(symbol); + uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob; + if (symbol == 0) + { + encoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + } + else + { + encoder.Low += newBound; + encoder.Range -= newBound; + Prob -= (Prob) >> kNumMoveBits; + } + if (encoder.Range < Encoder.kTopValue) + { + encoder.Range <<= 8; + encoder.ShiftLow(); + } + } + + private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits]; + + static BitEncoder() + { + const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + for (int i = kNumBits - 1; i >= 0; i--) + { + UInt32 start = (UInt32)1 << (kNumBits - i - 1); + UInt32 end = (UInt32)1 << (kNumBits - i); + for (UInt32 j = start; j < end; j++) + ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1)); + } + } + + public uint GetPrice(uint symbol) + { + return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; + } + public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; } + public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; } + } + + struct BitDecoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + + uint Prob; + + public void UpdateModel(int numMoveBits, uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> numMoveBits; + else + Prob -= (Prob) >> numMoveBits; + } + + public void Init() { Prob = kBitModelTotal >> 1; } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob; + if (rangeDecoder.Code < newBound) + { + rangeDecoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 0; + } + else + { + rangeDecoder.Range -= newBound; + rangeDecoder.Code -= newBound; + Prob -= (Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 1; + } + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs new file mode 100644 index 0000000..4b4506f --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs @@ -0,0 +1,157 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitTreeEncoder + { + BitEncoder[] Models; + int NumBitLevels; + + public BitTreeEncoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitEncoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public void Encode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + } + } + + public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (UInt32 i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + + public UInt32 GetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + price += Models[m].GetPrice(bit); + m = (m << 1) + bit; + } + return price; + } + + public UInt32 ReverseGetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex, + int NumBitLevels, UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[startIndex + m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex, + Encoder rangeEncoder, int NumBitLevels, UInt32 symbol) + { + UInt32 m = 1; + for (int i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[startIndex + m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + } + + struct BitTreeDecoder + { + BitDecoder[] Models; + int NumBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitDecoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--) + m = (m << 1) + Models[m].Decode(rangeDecoder); + return m - ((uint)1 << NumBitLevels); + } + + public uint ReverseDecode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex, + RangeCoder.Decoder rangeDecoder, int NumBitLevels) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[startIndex + m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + } +} diff --git a/3rdparty/physfs/lzma/CS/7zip/ICoder.cs b/3rdparty/physfs/lzma/CS/7zip/ICoder.cs new file mode 100644 index 0000000..85bf531 --- /dev/null +++ b/3rdparty/physfs/lzma/CS/7zip/ICoder.cs @@ -0,0 +1,145 @@ +// ICoder.h + +using System; + +namespace SevenZip +{ + /// + /// The exception that is thrown when an error in input stream occurs during decoding. + /// + class DataErrorException : ApplicationException + { + public DataErrorException(): base("Data Error") { } + } + + /// + /// The exception that is thrown when the value of an argument is outside the allowable range. + /// + class InvalidParamException : ApplicationException + { + public InvalidParamException(): base("Invalid Parameter") { } + } + + public interface ICodeProgress + { + /// + /// Callback progress. + /// + /// + /// input size. -1 if unknown. + /// + /// + /// output size. -1 if unknown. + /// + void SetProgress(Int64 inSize, Int64 outSize); + }; + + public interface ICoder + { + /// + /// Codes streams. + /// + /// + /// input Stream. + /// + /// + /// output Stream. + /// + /// + /// input Size. -1 if unknown. + /// + /// + /// output Size. -1 if unknown. + /// + /// + /// callback progress reference. + /// + /// + /// if input stream is not valid + /// + void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress); + }; + + /* + public interface ICoder2 + { + void Code(ISequentialInStream []inStreams, + const UInt64 []inSizes, + ISequentialOutStream []outStreams, + UInt64 []outSizes, + ICodeProgress progress); + }; + */ + + /// + /// Provides the fields that represent properties idenitifiers for compressing. + /// + public enum CoderPropID + { + /// + /// Specifies size of dictionary. + /// + DictionarySize = 0x400, + /// + /// Specifies size of memory for PPM*. + /// + UsedMemorySize, + /// + /// Specifies order for PPM methods. + /// + Order, + /// + /// Specifies number of postion state bits for LZMA (0 <= x <= 4). + /// + PosStateBits = 0x440, + /// + /// Specifies number of literal context bits for LZMA (0 <= x <= 8). + /// + LitContextBits, + /// + /// Specifies number of literal position bits for LZMA (0 <= x <= 4). + /// + LitPosBits, + /// + /// Specifies number of fast bytes for LZ*. + /// + NumFastBytes = 0x450, + /// + /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B". + /// + MatchFinder, + /// + /// Specifies number of passes. + /// + NumPasses = 0x460, + /// + /// Specifies number of algorithm. + /// + Algorithm = 0x470, + /// + /// Specifies multithread mode. + /// + MultiThread = 0x480, + /// + /// Specifies mode with end marker. + /// + EndMarker = 0x490 + }; + + + public interface ISetCoderProperties + { + void SetCoderProperties(CoderPropID[] propIDs, object[] properties); + }; + + public interface IWriteCoderProperties + { + void WriteCoderProperties(System.IO.Stream outStream); + } + + public interface ISetDecoderProperties + { + void SetDecoderProperties(byte[] properties); + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/CRC.java b/3rdparty/physfs/lzma/Java/SevenZip/CRC.java new file mode 100644 index 0000000..c55e33c --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/CRC.java @@ -0,0 +1,52 @@ +// SevenZip/CRC.java + +package SevenZip; + +public class CRC +{ + static public int[] Table = new int[256]; + + static + { + for (int i = 0; i < 256; i++) + { + int r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >>> 1) ^ 0xEDB88320; + else + r >>>= 1; + Table[i] = r; + } + } + + int _value = -1; + + public void Init() + { + _value = -1; + } + + public void Update(byte[] data, int offset, int size) + { + for (int i = 0; i < size; i++) + _value = Table[(_value ^ data[offset + i]) & 0xFF] ^ (_value >>> 8); + } + + public void Update(byte[] data) + { + int size = data.length; + for (int i = 0; i < size; i++) + _value = Table[(_value ^ data[i]) & 0xFF] ^ (_value >>> 8); + } + + public void UpdateByte(int b) + { + _value = Table[(_value ^ b) & 0xFF] ^ (_value >>> 8); + } + + public int GetDigest() + { + return _value ^ (-1); + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/BinTree.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/BinTree.java new file mode 100644 index 0000000..e2074e9 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/BinTree.java @@ -0,0 +1,382 @@ +// LZ.BinTree + +package SevenZip.Compression.LZ; +import java.io.IOException; + + +public class BinTree extends InWindow +{ + int _cyclicBufferPos; + int _cyclicBufferSize = 0; + int _matchMaxLen; + + int[] _son; + int[] _hash; + + int _cutValue = 0xFF; + int _hashMask; + int _hashSizeSum = 0; + + boolean HASH_ARRAY = true; + + static final int kHash2Size = 1 << 10; + static final int kHash3Size = 1 << 16; + static final int kBT2HashSize = 1 << 16; + static final int kStartMaxLen = 1; + static final int kHash3Offset = kHash2Size; + static final int kEmptyHashValue = 0; + static final int kMaxValForNormalize = (1 << 30) - 1; + + int kNumHashDirectBytes = 0; + int kMinMatchCheck = 4; + int kFixHashSize = kHash2Size + kHash3Size; + + public void SetType(int numHashBytes) + { + HASH_ARRAY = (numHashBytes > 2); + if (HASH_ARRAY) + { + kNumHashDirectBytes = 0; + kMinMatchCheck = 4; + kFixHashSize = kHash2Size + kHash3Size; + } + else + { + kNumHashDirectBytes = 2; + kMinMatchCheck = 2 + 1; + kFixHashSize = 0; + } + } + + + + + public void Init() throws IOException + { + super.Init(); + for (int i = 0; i < _hashSizeSum; i++) + _hash[i] = kEmptyHashValue; + _cyclicBufferPos = 0; + ReduceOffsets(-1); + } + + public void MovePos() throws IOException + { + if (++_cyclicBufferPos >= _cyclicBufferSize) + _cyclicBufferPos = 0; + super.MovePos(); + if (_pos == kMaxValForNormalize) + Normalize(); + } + + + + + + + + + public boolean Create(int historySize, int keepAddBufferBefore, + int matchMaxLen, int keepAddBufferAfter) + { + if (historySize > kMaxValForNormalize - 256) + return false; + _cutValue = 16 + (matchMaxLen >> 1); + + int windowReservSize = (historySize + keepAddBufferBefore + + matchMaxLen + keepAddBufferAfter) / 2 + 256; + + super.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize); + + _matchMaxLen = matchMaxLen; + + int cyclicBufferSize = historySize + 1; + if (_cyclicBufferSize != cyclicBufferSize) + _son = new int[(_cyclicBufferSize = cyclicBufferSize) * 2]; + + int hs = kBT2HashSize; + + if (HASH_ARRAY) + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) + hs >>= 1; + _hashMask = hs; + hs++; + hs += kFixHashSize; + } + if (hs != _hashSizeSum) + _hash = new int [_hashSizeSum = hs]; + return true; + } + public int GetMatches(int[] distances) throws IOException + { + int lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + return 0; + } + } + + int offset = 0; + int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + int cur = _bufferOffset + _pos; + int maxLen = kStartMaxLen; // to avoid items for len < hashSize; + int hashValue, hash2Value = 0, hash3Value = 0; + + if (HASH_ARRAY) + { + int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF); + hash2Value = temp & (kHash2Size - 1); + temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8); + hash3Value = temp & (kHash3Size - 1); + hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask; + } + else + hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8)); + + int curMatch = _hash[kFixHashSize + hashValue]; + if (HASH_ARRAY) + { + int curMatch2 = _hash[hash2Value]; + int curMatch3 = _hash[kHash3Offset + hash3Value]; + _hash[hash2Value] = _pos; + _hash[kHash3Offset + hash3Value] = _pos; + if (curMatch2 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) + { + distances[offset++] = maxLen = 2; + distances[offset++] = _pos - curMatch2 - 1; + } + if (curMatch3 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) + { + if (curMatch3 == curMatch2) + offset -= 2; + distances[offset++] = maxLen = 3; + distances[offset++] = _pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + if (offset != 0 && curMatch2 == curMatch) + { + offset -= 2; + maxLen = kStartMaxLen; + } + } + + _hash[kFixHashSize + hashValue] = _pos; + + int ptr0 = (_cyclicBufferPos << 1) + 1; + int ptr1 = (_cyclicBufferPos << 1); + + int len0, len1; + len0 = len1 = kNumHashDirectBytes; + + if (kNumHashDirectBytes != 0) + { + if (curMatch > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] != + _bufferBase[cur + kNumHashDirectBytes]) + { + distances[offset++] = maxLen = kNumHashDirectBytes; + distances[offset++] = _pos - curMatch - 1; + } + } + } + + int count = _cutValue; + + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + int delta = _pos - curMatch; + int cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + int pby1 = _bufferOffset + curMatch; + int len = Math.min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while(++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (maxLen < len) + { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + } + if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF)) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + return offset; + } + + public void Skip(int num) throws IOException + { + do + { + int lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + continue; + } + } + + int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + int cur = _bufferOffset + _pos; + + int hashValue; + + if (HASH_ARRAY) + { + int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF); + int hash2Value = temp & (kHash2Size - 1); + _hash[hash2Value] = _pos; + temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8); + int hash3Value = temp & (kHash3Size - 1); + _hash[kHash3Offset + hash3Value] = _pos; + hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask; + } + else + hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8)); + + int curMatch = _hash[kFixHashSize + hashValue]; + _hash[kFixHashSize + hashValue] = _pos; + + int ptr0 = (_cyclicBufferPos << 1) + 1; + int ptr1 = (_cyclicBufferPos << 1); + + int len0, len1; + len0 = len1 = kNumHashDirectBytes; + + int count = _cutValue; + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + + int delta = _pos - curMatch; + int cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + int pby1 = _bufferOffset + curMatch; + int len = Math.min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while (++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF)) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + } + while (--num != 0); + } + + void NormalizeLinks(int[] items, int numItems, int subValue) + { + for (int i = 0; i < numItems; i++) + { + int value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } + } + + void Normalize() + { + int subValue = _pos - _cyclicBufferSize; + NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); + NormalizeLinks(_hash, _hashSizeSum, subValue); + ReduceOffsets(subValue); + } + + public void SetCutValue(int cutValue) { _cutValue = cutValue; } + + private static final int[] CrcTable = new int[256]; + + static + { + for (int i = 0; i < 256; i++) + { + int r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >>> 1) ^ 0xEDB88320; + else + r >>>= 1; + CrcTable[i] = r; + } + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/InWindow.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/InWindow.java new file mode 100644 index 0000000..c9efe60 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/InWindow.java @@ -0,0 +1,131 @@ +// LZ.InWindow + +package SevenZip.Compression.LZ; + +import java.io.IOException; + +public class InWindow +{ + public byte[] _bufferBase; // pointer to buffer with data + java.io.InputStream _stream; + int _posLimit; // offset (from _buffer) of first byte when new block reading must be done + boolean _streamEndWasReached; // if (true) then _streamPos shows real end of stream + + int _pointerToLastSafePosition; + + public int _bufferOffset; + + public int _blockSize; // Size of Allocated memory block + public int _pos; // offset (from _buffer) of curent byte + int _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos + int _keepSizeAfter; // how many BYTEs must be kept buffer after _pos + public int _streamPos; // offset (from _buffer) of first not read byte from Stream + + public void MoveBlock() + { + int offset = _bufferOffset + _pos - _keepSizeBefore; + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) + offset--; + + int numBytes = _bufferOffset + _streamPos - offset; + + // check negative offset ???? + for (int i = 0; i < numBytes; i++) + _bufferBase[i] = _bufferBase[offset + i]; + _bufferOffset -= offset; + } + + public void ReadBlock() throws IOException + { + if (_streamEndWasReached) + return; + while (true) + { + int size = (0 - _bufferOffset) + _blockSize - _streamPos; + if (size == 0) + return; + int numReadBytes = _stream.read(_bufferBase, _bufferOffset + _streamPos, size); + if (numReadBytes == -1) + { + _posLimit = _streamPos; + int pointerToPostion = _bufferOffset + _posLimit; + if (pointerToPostion > _pointerToLastSafePosition) + _posLimit = _pointerToLastSafePosition - _bufferOffset; + + _streamEndWasReached = true; + return; + } + _streamPos += numReadBytes; + if (_streamPos >= _pos + _keepSizeAfter) + _posLimit = _streamPos - _keepSizeAfter; + } + } + + void Free() { _bufferBase = null; } + + public void Create(int keepSizeBefore, int keepSizeAfter, int keepSizeReserv) + { + _keepSizeBefore = keepSizeBefore; + _keepSizeAfter = keepSizeAfter; + int blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; + if (_bufferBase == null || _blockSize != blockSize) + { + Free(); + _blockSize = blockSize; + _bufferBase = new byte[_blockSize]; + } + _pointerToLastSafePosition = _blockSize - keepSizeAfter; + } + + public void SetStream(java.io.InputStream stream) { _stream = stream; } + public void ReleaseStream() { _stream = null; } + + public void Init() throws IOException + { + _bufferOffset = 0; + _pos = 0; + _streamPos = 0; + _streamEndWasReached = false; + ReadBlock(); + } + + public void MovePos() throws IOException + { + _pos++; + if (_pos > _posLimit) + { + int pointerToPostion = _bufferOffset + _pos; + if (pointerToPostion > _pointerToLastSafePosition) + MoveBlock(); + ReadBlock(); + } + } + + public byte GetIndexByte(int index) { return _bufferBase[_bufferOffset + _pos + index]; } + + // index + limit have not to exceed _keepSizeAfter; + public int GetMatchLen(int index, int distance, int limit) + { + if (_streamEndWasReached) + if ((_pos + index) + limit > _streamPos) + limit = _streamPos - (_pos + index); + distance++; + // Byte *pby = _buffer + (size_t)_pos + index; + int pby = _bufferOffset + _pos + index; + + int i; + for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++); + return i; + } + + public int GetNumAvailableBytes() { return _streamPos - _pos; } + + public void ReduceOffsets(int subValue) + { + _bufferOffset += subValue; + _posLimit -= subValue; + _pos -= subValue; + _streamPos -= subValue; + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/OutWindow.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/OutWindow.java new file mode 100644 index 0000000..2fd2832 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZ/OutWindow.java @@ -0,0 +1,85 @@ +// LZ.OutWindow + +package SevenZip.Compression.LZ; + +import java.io.IOException; + +public class OutWindow +{ + byte[] _buffer; + int _pos; + int _windowSize = 0; + int _streamPos; + java.io.OutputStream _stream; + + public void Create(int windowSize) + { + if (_buffer == null || _windowSize != windowSize) + _buffer = new byte[windowSize]; + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + } + + public void SetStream(java.io.OutputStream stream) throws IOException + { + ReleaseStream(); + _stream = stream; + } + + public void ReleaseStream() throws IOException + { + Flush(); + _stream = null; + } + + public void Init(boolean solid) + { + if (!solid) + { + _streamPos = 0; + _pos = 0; + } + } + + public void Flush() throws IOException + { + int size = _pos - _streamPos; + if (size == 0) + return; + _stream.write(_buffer, _streamPos, size); + if (_pos >= _windowSize) + _pos = 0; + _streamPos = _pos; + } + + public void CopyBlock(int distance, int len) throws IOException + { + int pos = _pos - distance - 1; + if (pos < 0) + pos += _windowSize; + for (; len != 0; len--) + { + if (pos >= _windowSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos >= _windowSize) + Flush(); + } + } + + public void PutByte(byte b) throws IOException + { + _buffer[_pos++] = b; + if (_pos >= _windowSize) + Flush(); + } + + public byte GetByte(int distance) + { + int pos = _pos - distance - 1; + if (pos < 0) + pos += _windowSize; + return _buffer[pos]; + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Base.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Base.java new file mode 100644 index 0000000..b4f2fb5 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Base.java @@ -0,0 +1,88 @@ +// Base.java + +package SevenZip.Compression.LZMA; + +public class Base +{ + public static final int kNumRepDistances = 4; + public static final int kNumStates = 12; + + public static final int StateInit() + { + return 0; + } + + public static final int StateUpdateChar(int index) + { + if (index < 4) + return 0; + if (index < 10) + return index - 3; + return index - 6; + } + + public static final int StateUpdateMatch(int index) + { + return (index < 7 ? 7 : 10); + } + + public static final int StateUpdateRep(int index) + { + return (index < 7 ? 8 : 11); + } + + public static final int StateUpdateShortRep(int index) + { + return (index < 7 ? 9 : 11); + } + + public static final boolean StateIsCharState(int index) + { + return index < 7; + } + + public static final int kNumPosSlotBits = 6; + public static final int kDicLogSizeMin = 0; + // public static final int kDicLogSizeMax = 28; + // public static final int kDistTableSizeMax = kDicLogSizeMax * 2; + + public static final int kNumLenToPosStatesBits = 2; // it's for speed optimization + public static final int kNumLenToPosStates = 1 << kNumLenToPosStatesBits; + + public static final int kMatchMinLen = 2; + + public static final int GetLenToPosState(int len) + { + len -= kMatchMinLen; + if (len < kNumLenToPosStates) + return len; + return (int)(kNumLenToPosStates - 1); + } + + public static final int kNumAlignBits = 4; + public static final int kAlignTableSize = 1 << kNumAlignBits; + public static final int kAlignMask = (kAlignTableSize - 1); + + public static final int kStartPosModelIndex = 4; + public static final int kEndPosModelIndex = 14; + public static final int kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + + public static final int kNumFullDistances = 1 << (kEndPosModelIndex / 2); + + public static final int kNumLitPosStatesBitsEncodingMax = 4; + public static final int kNumLitContextBitsMax = 8; + + public static final int kNumPosStatesBitsMax = 4; + public static final int kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + public static final int kNumPosStatesBitsEncodingMax = 4; + public static final int kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + public static final int kNumLowLenBits = 3; + public static final int kNumMidLenBits = 3; + public static final int kNumHighLenBits = 8; + public static final int kNumLowLenSymbols = 1 << kNumLowLenBits; + public static final int kNumMidLenSymbols = 1 << kNumMidLenBits; + public static final int kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + + (1 << kNumHighLenBits); + public static final int kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1; +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Decoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Decoder.java new file mode 100644 index 0000000..16ee249 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Decoder.java @@ -0,0 +1,329 @@ +package SevenZip.Compression.LZMA; + +import SevenZip.Compression.RangeCoder.BitTreeDecoder; +import SevenZip.Compression.LZMA.Base; +import SevenZip.Compression.LZ.OutWindow; +import java.io.IOException; + +public class Decoder +{ + class LenDecoder + { + short[] m_Choice = new short[2]; + BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); + int m_NumPosStates = 0; + + public void Create(int numPosStates) + { + for (; m_NumPosStates < numPosStates; m_NumPosStates++) + { + m_LowCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumLowLenBits); + m_MidCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumMidLenBits); + } + } + + public void Init() + { + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Choice); + for (int posState = 0; posState < m_NumPosStates; posState++) + { + m_LowCoder[posState].Init(); + m_MidCoder[posState].Init(); + } + m_HighCoder.Init(); + } + + public int Decode(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, int posState) throws IOException + { + if (rangeDecoder.DecodeBit(m_Choice, 0) == 0) + return m_LowCoder[posState].Decode(rangeDecoder); + int symbol = Base.kNumLowLenSymbols; + if (rangeDecoder.DecodeBit(m_Choice, 1) == 0) + symbol += m_MidCoder[posState].Decode(rangeDecoder); + else + symbol += Base.kNumMidLenSymbols + m_HighCoder.Decode(rangeDecoder); + return symbol; + } + } + + class LiteralDecoder + { + class Decoder2 + { + short[] m_Decoders = new short[0x300]; + + public void Init() + { + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Decoders); + } + + public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder) throws IOException + { + int symbol = 1; + do + symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol); + while (symbol < 0x100); + return (byte)symbol; + } + + public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, byte matchByte) throws IOException + { + int symbol = 1; + do + { + int matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + int bit = rangeDecoder.DecodeBit(m_Decoders, ((1 + matchBit) << 8) + symbol); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol); + break; + } + } + while (symbol < 0x100); + return (byte)symbol; + } + } + + Decoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + int m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = (1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + int numStates = 1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Decoder2[numStates]; + for (int i = 0; i < numStates; i++) + m_Coders[i] = new Decoder2(); + } + + public void Init() + { + int numStates = 1 << (m_NumPrevBits + m_NumPosBits); + for (int i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + Decoder2 GetDecoder(int pos, byte prevByte) + { + return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + ((prevByte & 0xFF) >>> (8 - m_NumPrevBits))]; + } + } + + OutWindow m_OutWindow = new OutWindow(); + SevenZip.Compression.RangeCoder.Decoder m_RangeDecoder = new SevenZip.Compression.RangeCoder.Decoder(); + + short[] m_IsMatchDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax]; + short[] m_IsRepDecoders = new short[Base.kNumStates]; + short[] m_IsRepG0Decoders = new short[Base.kNumStates]; + short[] m_IsRepG1Decoders = new short[Base.kNumStates]; + short[] m_IsRepG2Decoders = new short[Base.kNumStates]; + short[] m_IsRep0LongDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; + short[] m_PosDecoders = new short[Base.kNumFullDistances - Base.kEndPosModelIndex]; + + BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + + LenDecoder m_LenDecoder = new LenDecoder(); + LenDecoder m_RepLenDecoder = new LenDecoder(); + + LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + + int m_DictionarySize = -1; + int m_DictionarySizeCheck = -1; + + int m_PosStateMask; + + public Decoder() + { + for (int i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + } + + boolean SetDictionarySize(int dictionarySize) + { + if (dictionarySize < 0) + return false; + if (m_DictionarySize != dictionarySize) + { + m_DictionarySize = dictionarySize; + m_DictionarySizeCheck = Math.max(m_DictionarySize, 1); + m_OutWindow.Create(Math.max(m_DictionarySizeCheck, (1 << 12))); + } + return true; + } + + boolean SetLcLpPb(int lc, int lp, int pb) + { + if (lc > Base.kNumLitContextBitsMax || lp > 4 || pb > Base.kNumPosStatesBitsMax) + return false; + m_LiteralDecoder.Create(lp, lc); + int numPosStates = 1 << pb; + m_LenDecoder.Create(numPosStates); + m_RepLenDecoder.Create(numPosStates); + m_PosStateMask = numPosStates - 1; + return true; + } + + void Init() throws IOException + { + m_OutWindow.Init(false); + + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsMatchDecoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRep0LongDecoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepDecoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG0Decoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG1Decoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG2Decoders); + SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_PosDecoders); + + m_LiteralDecoder.Init(); + int i; + for (i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + m_LenDecoder.Init(); + m_RepLenDecoder.Init(); + m_PosAlignDecoder.Init(); + m_RangeDecoder.Init(); + } + + public boolean Code(java.io.InputStream inStream, java.io.OutputStream outStream, + long outSize) throws IOException + { + m_RangeDecoder.SetStream(inStream); + m_OutWindow.SetStream(outStream); + Init(); + + int state = Base.StateInit(); + int rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0; + + long nowPos64 = 0; + byte prevByte = 0; + while (outSize < 0 || nowPos64 < outSize) + { + int posState = (int)nowPos64 & m_PosStateMask; + if (m_RangeDecoder.DecodeBit(m_IsMatchDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0) + { + LiteralDecoder.Decoder2 decoder2 = m_LiteralDecoder.GetDecoder((int)nowPos64, prevByte); + if (!Base.StateIsCharState(state)) + prevByte = decoder2.DecodeWithMatchByte(m_RangeDecoder, m_OutWindow.GetByte(rep0)); + else + prevByte = decoder2.DecodeNormal(m_RangeDecoder); + m_OutWindow.PutByte(prevByte); + state = Base.StateUpdateChar(state); + nowPos64++; + } + else + { + int len; + if (m_RangeDecoder.DecodeBit(m_IsRepDecoders, state) == 1) + { + len = 0; + if (m_RangeDecoder.DecodeBit(m_IsRepG0Decoders, state) == 0) + { + if (m_RangeDecoder.DecodeBit(m_IsRep0LongDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0) + { + state = Base.StateUpdateShortRep(state); + len = 1; + } + } + else + { + int distance; + if (m_RangeDecoder.DecodeBit(m_IsRepG1Decoders, state) == 0) + distance = rep1; + else + { + if (m_RangeDecoder.DecodeBit(m_IsRepG2Decoders, state) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + if (len == 0) + { + len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen; + state = Base.StateUpdateRep(state); + } + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState); + state = Base.StateUpdateMatch(state); + int posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder); + if (posSlot >= Base.kStartPosModelIndex) + { + int numDirectBits = (posSlot >> 1) - 1; + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.kEndPosModelIndex) + rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 - posSlot - 1, m_RangeDecoder, numDirectBits); + else + { + rep0 += (m_RangeDecoder.DecodeDirectBits( + numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); + rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); + if (rep0 < 0) + { + if (rep0 == -1) + break; + return false; + } + } + } + else + rep0 = posSlot; + } + if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck) + { + // m_OutWindow.Flush(); + return false; + } + m_OutWindow.CopyBlock(rep0, len); + nowPos64 += len; + prevByte = m_OutWindow.GetByte(0); + } + } + m_OutWindow.Flush(); + m_OutWindow.ReleaseStream(); + m_RangeDecoder.ReleaseStream(); + return true; + } + + public boolean SetDecoderProperties(byte[] properties) + { + if (properties.length < 5) + return false; + int val = properties[0] & 0xFF; + int lc = val % 9; + int remainder = val / 9; + int lp = remainder % 5; + int pb = remainder / 5; + int dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((int)(properties[1 + i]) & 0xFF) << (i * 8); + if (!SetLcLpPb(lc, lp, pb)) + return false; + return SetDictionarySize(dictionarySize); + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Encoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Encoder.java new file mode 100644 index 0000000..71fd6e5 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/LZMA/Encoder.java @@ -0,0 +1,1416 @@ +package SevenZip.Compression.LZMA; + +import SevenZip.Compression.RangeCoder.BitTreeEncoder; +import SevenZip.Compression.LZMA.Base; +import SevenZip.Compression.LZ.BinTree; +import SevenZip.ICodeProgress; +import java.io.IOException; + +public class Encoder +{ + public static final int EMatchFinderTypeBT2 = 0; + public static final int EMatchFinderTypeBT4 = 1; + + + + + static final int kIfinityPrice = 0xFFFFFFF; + + static byte[] g_FastPos = new byte[1 << 11]; + + static + { + int kFastSlots = 22; + int c = 2; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + for (int slotFast = 2; slotFast < kFastSlots; slotFast++) + { + int k = (1 << ((slotFast >> 1) - 1)); + for (int j = 0; j < k; j++, c++) + g_FastPos[c] = (byte)slotFast; + } + } + + static int GetPosSlot(int pos) + { + if (pos < (1 << 11)) + return g_FastPos[pos]; + if (pos < (1 << 21)) + return (g_FastPos[pos >> 10] + 20); + return (g_FastPos[pos >> 20] + 40); + } + + static int GetPosSlot2(int pos) + { + if (pos < (1 << 17)) + return (g_FastPos[pos >> 6] + 12); + if (pos < (1 << 27)) + return (g_FastPos[pos >> 16] + 32); + return (g_FastPos[pos >> 26] + 52); + } + + int _state = Base.StateInit(); + byte _previousByte; + int[] _repDistances = new int[Base.kNumRepDistances]; + + void BaseInit() + { + _state = Base.StateInit(); + _previousByte = 0; + for (int i = 0; i < Base.kNumRepDistances; i++) + _repDistances[i] = 0; + } + + static final int kDefaultDictionaryLogSize = 22; + static final int kNumFastBytesDefault = 0x20; + + class LiteralEncoder + { + class Encoder2 + { + short[] m_Encoders = new short[0x300]; + + public void Init() { SevenZip.Compression.RangeCoder.Encoder.InitBitModels(m_Encoders); } + + + + public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte symbol) throws IOException + { + int context = 1; + for (int i = 7; i >= 0; i--) + { + int bit = ((symbol >> i) & 1); + rangeEncoder.Encode(m_Encoders, context, bit); + context = (context << 1) | bit; + } + } + + public void EncodeMatched(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) throws IOException + { + int context = 1; + boolean same = true; + for (int i = 7; i >= 0; i--) + { + int bit = ((symbol >> i) & 1); + int state = context; + if (same) + { + int matchBit = ((matchByte >> i) & 1); + state += ((1 + matchBit) << 8); + same = (matchBit == bit); + } + rangeEncoder.Encode(m_Encoders, state, bit); + context = (context << 1) | bit; + } + } + + public int GetPrice(boolean matchMode, byte matchByte, byte symbol) + { + int price = 0; + int context = 1; + int i = 7; + if (matchMode) + { + for (; i >= 0; i--) + { + int matchBit = (matchByte >> i) & 1; + int bit = (symbol >> i) & 1; + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(m_Encoders[((1 + matchBit) << 8) + context], bit); + context = (context << 1) | bit; + if (matchBit != bit) + { + i--; + break; + } + } + } + for (; i >= 0; i--) + { + int bit = (symbol >> i) & 1; + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(m_Encoders[context], bit); + context = (context << 1) | bit; + } + return price; + } + } + + Encoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + int m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = (1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + int numStates = 1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Encoder2[numStates]; + for (int i = 0; i < numStates; i++) + m_Coders[i] = new Encoder2(); + } + + public void Init() + { + int numStates = 1 << (m_NumPrevBits + m_NumPosBits); + for (int i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + public Encoder2 GetSubCoder(int pos, byte prevByte) + { return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + ((prevByte & 0xFF) >>> (8 - m_NumPrevBits))]; } + } + + class LenEncoder + { + short[] _choice = new short[2]; + BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + BitTreeEncoder _highCoder = new BitTreeEncoder(Base.kNumHighLenBits); + + + public LenEncoder() + { + for (int posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) + { + _lowCoder[posState] = new BitTreeEncoder(Base.kNumLowLenBits); + _midCoder[posState] = new BitTreeEncoder(Base.kNumMidLenBits); + } + } + + public void Init(int numPosStates) + { + SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_choice); + + for (int posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + + public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, int symbol, int posState) throws IOException + { + if (symbol < Base.kNumLowLenSymbols) + { + rangeEncoder.Encode(_choice, 0, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + symbol -= Base.kNumLowLenSymbols; + rangeEncoder.Encode(_choice, 0, 1); + if (symbol < Base.kNumMidLenSymbols) + { + rangeEncoder.Encode(_choice, 1, 0); + _midCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + rangeEncoder.Encode(_choice, 1, 1); + _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + } + } + } + + public void SetPrices(int posState, int numSymbols, int[] prices, int st) + { + int a0 = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_choice[0]); + int a1 = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_choice[0]); + int b0 = a1 + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_choice[1]); + int b1 = a1 + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_choice[1]); + int i = 0; + for (i = 0; i < Base.kNumLowLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols); + } + for (; i < numSymbols; i++) + prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + } + }; + + public static final int kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + + class LenPriceTableEncoder extends LenEncoder + { + int[] _prices = new int[Base.kNumLenSymbols< 0) + { + lenRes = _matchDistances[_numDistancePairs - 2]; + if (lenRes == _numFastBytes) + lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[_numDistancePairs - 1], + Base.kMatchMaxLen - lenRes); + } + _additionalOffset++; + return lenRes; + } + + void MovePos(int num) throws java.io.IOException + { + if (num > 0) + { + _matchFinder.Skip(num); + _additionalOffset += num; + } + } + + int GetRepLen1Price(int state, int posState) + { + return SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG0[state]) + + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]); + } + + int GetPureRepPrice(int repIndex, int state, int posState) + { + int price; + if (repIndex == 0) + { + price = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG0[state]); + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]); + } + else + { + price = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRepG0[state]); + if (repIndex == 1) + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG1[state]); + else + { + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRepG1[state]); + price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(_isRepG2[state], repIndex - 2); + } + } + return price; + } + + int GetRepPrice(int repIndex, int len, int state, int posState) + { + int price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + return price + GetPureRepPrice(repIndex, state, posState); + } + + int GetPosLenPrice(int pos, int len, int posState) + { + int price; + int lenToPosState = Base.GetLenToPosState(len); + if (pos < Base.kNumFullDistances) + price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + else + price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.kAlignMask]; + return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + } + + int Backward(int cur) + { + _optimumEndIndex = cur; + int posMem = _optimum[cur].PosPrev; + int backMem = _optimum[cur].BackPrev; + do + { + if (_optimum[cur].Prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem].PosPrev = posMem - 1; + if (_optimum[cur].Prev2) + { + _optimum[posMem - 1].Prev1IsChar = false; + _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; + _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + } + } + int posPrev = posMem; + int backCur = backMem; + + backMem = _optimum[posPrev].BackPrev; + posMem = _optimum[posPrev].PosPrev; + + _optimum[posPrev].BackPrev = backCur; + _optimum[posPrev].PosPrev = cur; + cur = posPrev; + } + while (cur > 0); + backRes = _optimum[0].BackPrev; + _optimumCurrentIndex = _optimum[0].PosPrev; + return _optimumCurrentIndex; + } + + int[] reps = new int[Base.kNumRepDistances]; + int[] repLens = new int[Base.kNumRepDistances]; + int backRes; + + int GetOptimum(int position) throws IOException + { + if (_optimumEndIndex != _optimumCurrentIndex) + { + int lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex].BackPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + int lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + lenMain = ReadMatchDistances(); + } + else + { + lenMain = _longestMatchLength; + _longestMatchWasFound = false; + } + numDistancePairs = _numDistancePairs; + + int numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; + if (numAvailableBytes < 2) + { + backRes = -1; + return 1; + } + if (numAvailableBytes > Base.kMatchMaxLen) + numAvailableBytes = Base.kMatchMaxLen; + + int repMaxIndex = 0; + int i; + for (i = 0; i < Base.kNumRepDistances; i++) + { + reps[i] = _repDistances[i]; + repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen); + if (repLens[i] > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + int lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + if (lenMain >= _numFastBytes) + { + backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + + byte currentByte = _matchFinder.GetIndexByte(0 - 1); + byte matchByte = _matchFinder.GetIndexByte(0 - _repDistances[0] - 1 - 1); + + if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + { + backRes = -1; + return 1; + } + + _optimum[0].State = _state; + + int posState = (position & _posStateMask); + + _optimum[1].Price = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(_state << Base.kNumPosStatesBitsMax) + posState]) + + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!Base.StateIsCharState(_state), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + int matchPrice = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(_state << Base.kNumPosStatesBitsMax) + posState]); + int repMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[_state]); + + if (matchByte == currentByte) + { + int shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if (shortRepPrice < _optimum[1].Price) + { + _optimum[1].Price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + + int lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + backRes = _optimum[1].BackPrev; + return 1; + } + + _optimum[1].PosPrev = 0; + + _optimum[0].Backs0 = reps[0]; + _optimum[0].Backs1 = reps[1]; + _optimum[0].Backs2 = reps[2]; + _optimum[0].Backs3 = reps[3]; + + int len = lenEnd; + do + _optimum[len--].Price = kIfinityPrice; + while (len >= 2); + + for (i = 0; i < Base.kNumRepDistances; i++) + { + int repLen = repLens[i]; + if (repLen < 2) + continue; + int price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + int curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + Optimal optimum = _optimum[repLen]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = i; + optimum.Prev1IsChar = false; + } + } + while (--repLen >= 2); + } + + int normalMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep[_state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= lenMain) + { + int offs = 0; + while (len > _matchDistances[offs]) + offs += 2; + for (; ; len++) + { + int distance = _matchDistances[offs + 1]; + int curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); + Optimal optimum = _optimum[len]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = distance + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + if (len == _matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + + int cur = 0; + + while (true) + { + cur++; + if (cur == lenEnd) + return Backward(cur); + int newLen = ReadMatchDistances(); + numDistancePairs = _numDistancePairs; + if (newLen >= _numFastBytes) + { + + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(cur); + } + position++; + int posPrev = _optimum[cur].PosPrev; + int state; + if (_optimum[cur].Prev1IsChar) + { + posPrev--; + if (_optimum[cur].Prev2) + { + state = _optimum[_optimum[cur].PosPrev2].State; + if (_optimum[cur].BackPrev2 < Base.kNumRepDistances) + state = Base.StateUpdateRep(state); + else + state = Base.StateUpdateMatch(state); + } + else + state = _optimum[posPrev].State; + state = Base.StateUpdateChar(state); + } + else + state = _optimum[posPrev].State; + if (posPrev == cur - 1) + { + if (_optimum[cur].IsShortRep()) + state = Base.StateUpdateShortRep(state); + else + state = Base.StateUpdateChar(state); + } + else + { + int pos; + if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2) + { + posPrev = _optimum[cur].PosPrev2; + pos = _optimum[cur].BackPrev2; + state = Base.StateUpdateRep(state); + } + else + { + pos = _optimum[cur].BackPrev; + if (pos < Base.kNumRepDistances) + state = Base.StateUpdateRep(state); + else + state = Base.StateUpdateMatch(state); + } + Optimal opt = _optimum[posPrev]; + if (pos < Base.kNumRepDistances) + { + if (pos == 0) + { + reps[0] = opt.Backs0; + reps[1] = opt.Backs1; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 1) + { + reps[0] = opt.Backs1; + reps[1] = opt.Backs0; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 2) + { + reps[0] = opt.Backs2; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs3; + } + else + { + reps[0] = opt.Backs3; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + else + { + reps[0] = (pos - Base.kNumRepDistances); + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + _optimum[cur].State = state; + _optimum[cur].Backs0 = reps[0]; + _optimum[cur].Backs1 = reps[1]; + _optimum[cur].Backs2 = reps[2]; + _optimum[cur].Backs3 = reps[3]; + int curPrice = _optimum[cur].Price; + + currentByte = _matchFinder.GetIndexByte(0 - 1); + matchByte = _matchFinder.GetIndexByte(0 - reps[0] - 1 - 1); + + posState = (position & _posStateMask); + + int curAnd1Price = curPrice + + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state << Base.kNumPosStatesBitsMax) + posState]) + + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)). + GetPrice(!Base.StateIsCharState(state), matchByte, currentByte); + + Optimal nextOptimum = _optimum[cur + 1]; + + boolean nextIsChar = false; + if (curAnd1Price < nextOptimum.Price) + { + nextOptimum.Price = curAnd1Price; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + matchPrice = curPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state << Base.kNumPosStatesBitsMax) + posState]); + repMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state]); + + if (matchByte == currentByte && + !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + { + int shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if (shortRepPrice <= nextOptimum.Price) + { + nextOptimum.Price = shortRepPrice; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsShortRep(); + nextIsChar = true; + } + } + + int numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; + numAvailableBytesFull = Math.min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) + continue; + if (numAvailableBytes > _numFastBytes) + numAvailableBytes = _numFastBytes; + if (!nextIsChar && matchByte != currentByte) + { + // try Literal + rep0 + int t = Math.min(numAvailableBytesFull - 1, _numFastBytes); + int lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t); + if (lenTest2 >= 2) + { + int state2 = Base.StateUpdateChar(state); + + int posStateNext = (position + 1) & _posStateMask; + int nextRepMatchPrice = curAnd1Price + + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) + + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]); + { + int offset = cur + 1 + lenTest2; + while (lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + int curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = false; + } + } + } + } + + int startLen = 2; // speed optimization + + for (int repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) + { + int lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes); + if (lenTest < 2) + continue; + int lenTestTemp = lenTest; + do + { + while (lenEnd < cur + lenTest) + _optimum[++lenEnd].Price = kIfinityPrice; + int curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = repIndex; + optimum.Prev1IsChar = false; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) + { + int t = Math.min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + int lenTest2 = _matchFinder.GetMatchLen(lenTest, reps[repIndex], t); + if (lenTest2 >= 2) + { + int state2 = Base.StateUpdateRep(state); + + int posStateNext = (position + lenTest) & _posStateMask; + int curAndLenCharPrice = + repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte(lenTest - 1 - 1)).GetPrice(true, + _matchFinder.GetIndexByte(lenTest - 1 - (reps[repIndex] + 1)), + _matchFinder.GetIndexByte(lenTest - 1)); + state2 = Base.StateUpdateChar(state2); + posStateNext = (position + lenTest + 1) & _posStateMask; + int nextMatchPrice = curAndLenCharPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]); + int nextRepMatchPrice = nextMatchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]); + + // for(; lenTest2 >= 2; lenTest2--) + { + int offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + int curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = repIndex; + } + } + } + } + } + + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ; + _matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + normalMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep[state]); + while (lenEnd < cur + newLen) + _optimum[++lenEnd].Price = kIfinityPrice; + + int offs = 0; + while (startLen > _matchDistances[offs]) + offs += 2; + + for (int lenTest = startLen; ; lenTest++) + { + int curBack = _matchDistances[offs + 1]; + int curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = curBack + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + + if (lenTest == _matchDistances[offs]) + { + if (lenTest < numAvailableBytesFull) + { + int t = Math.min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + int lenTest2 = _matchFinder.GetMatchLen(lenTest, curBack, t); + if (lenTest2 >= 2) + { + int state2 = Base.StateUpdateMatch(state); + + int posStateNext = (position + lenTest) & _posStateMask; + int curAndLenCharPrice = curAndLenPrice + + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte(lenTest - 1 - 1)). + GetPrice(true, + _matchFinder.GetIndexByte(lenTest - (curBack + 1) - 1), + _matchFinder.GetIndexByte(lenTest - 1)); + state2 = Base.StateUpdateChar(state2); + posStateNext = (position + lenTest + 1) & _posStateMask; + int nextMatchPrice = curAndLenCharPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]); + int nextRepMatchPrice = nextMatchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]); + + int offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = curBack + Base.kNumRepDistances; + } + } + } + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + } + } + + boolean ChangePair(int smallDist, int bigDist) + { + int kDif = 7; + return (smallDist < (1 << (32 - kDif)) && bigDist >= (smallDist << kDif)); + } + + void WriteEndMarker(int posState) throws IOException + { + if (!_writeEndMark) + return; + + _rangeEncoder.Encode(_isMatch, (_state << Base.kNumPosStatesBitsMax) + posState, 1); + _rangeEncoder.Encode(_isRep, _state, 0); + _state = Base.StateUpdateMatch(_state); + int len = Base.kMatchMinLen; + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + int posSlot = (1 << Base.kNumPosSlotBits) - 1; + int lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + int footerBits = 30; + int posReduced = (1 << footerBits) - 1; + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + } + + void Flush(int nowPos) throws IOException + { + ReleaseMFStream(); + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + _rangeEncoder.FlushStream(); + } + + public void CodeOneBlock(long[] inSize, long[] outSize, boolean[] finished) throws IOException + { + inSize[0] = 0; + outSize[0] = 0; + finished[0] = true; + + if (_inStream != null) + { + _matchFinder.SetStream(_inStream); + _matchFinder.Init(); + _needReleaseMFStream = true; + _inStream = null; + } + + if (_finished) + return; + _finished = true; + + + long progressPosValuePrev = nowPos64; + if (nowPos64 == 0) + { + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((int)nowPos64); + return; + } + + ReadMatchDistances(); + int posState = (int)(nowPos64) & _posStateMask; + _rangeEncoder.Encode(_isMatch, (_state << Base.kNumPosStatesBitsMax) + posState, 0); + _state = Base.StateUpdateChar(_state); + byte curByte = _matchFinder.GetIndexByte(0 - _additionalOffset); + _literalEncoder.GetSubCoder((int)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + nowPos64++; + } + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((int)nowPos64); + return; + } + while (true) + { + + int len = GetOptimum((int)nowPos64); + int pos = backRes; + int posState = ((int)nowPos64) & _posStateMask; + int complexState = (_state << Base.kNumPosStatesBitsMax) + posState; + if (len == 1 && pos == -1) + { + _rangeEncoder.Encode(_isMatch, complexState, 0); + byte curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); + LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((int)nowPos64, _previousByte); + if (!Base.StateIsCharState(_state)) + { + byte matchByte = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - _additionalOffset)); + subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); + } + else + subCoder.Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _state = Base.StateUpdateChar(_state); + } + else + { + _rangeEncoder.Encode(_isMatch, complexState, 1); + if (pos < Base.kNumRepDistances) + { + _rangeEncoder.Encode(_isRep, _state, 1); + if (pos == 0) + { + _rangeEncoder.Encode(_isRepG0, _state, 0); + if (len == 1) + _rangeEncoder.Encode(_isRep0Long, complexState, 0); + else + _rangeEncoder.Encode(_isRep0Long, complexState, 1); + } + else + { + _rangeEncoder.Encode(_isRepG0, _state, 1); + if (pos == 1) + _rangeEncoder.Encode(_isRepG1, _state, 0); + else + { + _rangeEncoder.Encode(_isRepG1, _state, 1); + _rangeEncoder.Encode(_isRepG2, _state, pos - 2); + } + } + if (len == 1) + _state = Base.StateUpdateShortRep(_state); + else + { + _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + _state = Base.StateUpdateRep(_state); + } + int distance = _repDistances[pos]; + if (pos != 0) + { + for (int i = pos; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + } + } + else + { + _rangeEncoder.Encode(_isRep, _state, 0); + _state = Base.StateUpdateMatch(_state); + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + pos -= Base.kNumRepDistances; + int posSlot = GetPosSlot(pos); + int lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + + if (posSlot >= Base.kStartPosModelIndex) + { + int footerBits = (int)((posSlot >> 1) - 1); + int baseVal = ((2 | (posSlot & 1)) << footerBits); + int posReduced = pos - baseVal; + + if (posSlot < Base.kEndPosModelIndex) + BitTreeEncoder.ReverseEncode(_posEncoders, + baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced); + else + { + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _alignPriceCount++; + } + } + int distance = pos; + for (int i = Base.kNumRepDistances - 1; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte(len - 1 - _additionalOffset); + } + _additionalOffset -= len; + nowPos64 += len; + if (_additionalOffset == 0) + { + // if (!_fastMode) + if (_matchPriceCount >= (1 << 7)) + FillDistancesPrices(); + if (_alignPriceCount >= Base.kAlignTableSize) + FillAlignPrices(); + inSize[0] = nowPos64; + outSize[0] = _rangeEncoder.GetProcessedSizeAdd(); + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((int)nowPos64); + return; + } + + if (nowPos64 - progressPosValuePrev >= (1 << 12)) + { + _finished = false; + finished[0] = false; + return; + } + } + } + } + + void ReleaseMFStream() + { + if (_matchFinder != null && _needReleaseMFStream) + { + _matchFinder.ReleaseStream(); + _needReleaseMFStream = false; + } + } + + void SetOutStream(java.io.OutputStream outStream) + { _rangeEncoder.SetStream(outStream); } + void ReleaseOutStream() + { _rangeEncoder.ReleaseStream(); } + + void ReleaseStreams() + { + ReleaseMFStream(); + ReleaseOutStream(); + } + + void SetStreams(java.io.InputStream inStream, java.io.OutputStream outStream, + long inSize, long outSize) + { + _inStream = inStream; + _finished = false; + Create(); + SetOutStream(outStream); + Init(); + + // if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _lenEncoder.UpdateTables(1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _repMatchLenEncoder.UpdateTables(1 << _posStateBits); + + nowPos64 = 0; + } + + long[] processedInSize = new long[1]; long[] processedOutSize = new long[1]; boolean[] finished = new boolean[1]; + public void Code(java.io.InputStream inStream, java.io.OutputStream outStream, + long inSize, long outSize, ICodeProgress progress) throws IOException + { + _needReleaseMFStream = false; + try + { + SetStreams(inStream, outStream, inSize, outSize); + while (true) + { + + + + CodeOneBlock(processedInSize, processedOutSize, finished); + if (finished[0]) + return; + if (progress != null) + { + progress.SetProgress(processedInSize[0], processedOutSize[0]); + } + } + } + finally + { + ReleaseStreams(); + } + } + + public static final int kPropSize = 5; + byte[] properties = new byte[kPropSize]; + + public void WriteCoderProperties(java.io.OutputStream outStream) throws IOException + { + properties[0] = (byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + for (int i = 0; i < 4; i++) + properties[1 + i] = (byte)(_dictionarySize >> (8 * i)); + outStream.write(properties, 0, kPropSize); + } + + int[] tempPrices = new int[Base.kNumFullDistances]; + int _matchPriceCount; + + void FillDistancesPrices() + { + for (int i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) + { + int posSlot = GetPosSlot(i); + int footerBits = (int)((posSlot >> 1) - 1); + int baseVal = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, + baseVal - posSlot - 1, footerBits, i - baseVal); + } + + for (int lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) + { + int posSlot; + BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; + + int st = (lenToPosState << Base.kNumPosSlotBits); + for (posSlot = 0; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); + for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << SevenZip.Compression.RangeCoder.Encoder.kNumBitPriceShiftBits); + + int st2 = lenToPosState * Base.kNumFullDistances; + int i; + for (i = 0; i < Base.kStartPosModelIndex; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + i]; + for (; i < Base.kNumFullDistances; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i]; + } + _matchPriceCount = 0; + } + + void FillAlignPrices() + { + for (int i = 0; i < Base.kAlignTableSize; i++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _alignPriceCount = 0; + } + + + public boolean SetAlgorithm(int algorithm) + { + /* + _fastMode = (algorithm == 0); + _maxMode = (algorithm >= 2); + */ + return true; + } + + public boolean SetDictionarySize(int dictionarySize) + { + int kDicLogSizeMaxCompress = 29; + if (dictionarySize < (1 << Base.kDicLogSizeMin) || dictionarySize > (1 << kDicLogSizeMaxCompress)) + return false; + _dictionarySize = dictionarySize; + int dicLogSize; + for (dicLogSize = 0; dictionarySize > (1 << dicLogSize); dicLogSize++) ; + _distTableSize = dicLogSize * 2; + return true; + } + + public boolean SeNumFastBytes(int numFastBytes) + { + if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) + return false; + _numFastBytes = numFastBytes; + return true; + } + + public boolean SetMatchFinder(int matchFinderIndex) + { + if (matchFinderIndex < 0 || matchFinderIndex > 2) + return false; + int matchFinderIndexPrev = _matchFinderType; + _matchFinderType = matchFinderIndex; + if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) + { + _dictionarySizePrev = -1; + _matchFinder = null; + } + return true; + } + + public boolean SetLcLpPb(int lc, int lp, int pb) + { + if ( + lp < 0 || lp > Base.kNumLitPosStatesBitsEncodingMax || + lc < 0 || lc > Base.kNumLitContextBitsMax || + pb < 0 || pb > Base.kNumPosStatesBitsEncodingMax) + return false; + _numLiteralPosStateBits = lp; + _numLiteralContextBits = lc; + _posStateBits = pb; + _posStateMask = ((1) << _posStateBits) - 1; + return true; + } + + public void SetEndMarkerMode(boolean endMarkerMode) + { + _writeEndMark = endMarkerMode; + } +} + diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java new file mode 100644 index 0000000..7698581 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java @@ -0,0 +1,55 @@ +package SevenZip.Compression.RangeCoder; + +public class BitTreeDecoder +{ + short[] Models; + int NumBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new short[1 << numBitLevels]; + } + + public void Init() + { + Decoder.InitBitModels(Models); + } + + public int Decode(Decoder rangeDecoder) throws java.io.IOException + { + int m = 1; + for (int bitIndex = NumBitLevels; bitIndex != 0; bitIndex--) + m = (m << 1) + rangeDecoder.DecodeBit(Models, m); + return m - (1 << NumBitLevels); + } + + public int ReverseDecode(Decoder rangeDecoder) throws java.io.IOException + { + int m = 1; + int symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + int bit = rangeDecoder.DecodeBit(Models, m); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static int ReverseDecode(short[] Models, int startIndex, + Decoder rangeDecoder, int NumBitLevels) throws java.io.IOException + { + int m = 1; + int symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + int bit = rangeDecoder.DecodeBit(Models, startIndex + m); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java new file mode 100644 index 0000000..f9e97db --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java @@ -0,0 +1,99 @@ +package SevenZip.Compression.RangeCoder; +import java.io.IOException; + +public class BitTreeEncoder +{ + short[] Models; + int NumBitLevels; + + public BitTreeEncoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new short[1 << numBitLevels]; + } + + public void Init() + { + Decoder.InitBitModels(Models); + } + + public void Encode(Encoder rangeEncoder, int symbol) throws IOException + { + int m = 1; + for (int bitIndex = NumBitLevels; bitIndex != 0; ) + { + bitIndex--; + int bit = (symbol >>> bitIndex) & 1; + rangeEncoder.Encode(Models, m, bit); + m = (m << 1) | bit; + } + } + + public void ReverseEncode(Encoder rangeEncoder, int symbol) throws IOException + { + int m = 1; + for (int i = 0; i < NumBitLevels; i++) + { + int bit = symbol & 1; + rangeEncoder.Encode(Models, m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + + public int GetPrice(int symbol) + { + int price = 0; + int m = 1; + for (int bitIndex = NumBitLevels; bitIndex != 0; ) + { + bitIndex--; + int bit = (symbol >>> bitIndex) & 1; + price += Encoder.GetPrice(Models[m], bit); + m = (m << 1) + bit; + } + return price; + } + + public int ReverseGetPrice(int symbol) + { + int price = 0; + int m = 1; + for (int i = NumBitLevels; i != 0; i--) + { + int bit = symbol & 1; + symbol >>>= 1; + price += Encoder.GetPrice(Models[m], bit); + m = (m << 1) | bit; + } + return price; + } + + public static int ReverseGetPrice(short[] Models, int startIndex, + int NumBitLevels, int symbol) + { + int price = 0; + int m = 1; + for (int i = NumBitLevels; i != 0; i--) + { + int bit = symbol & 1; + symbol >>>= 1; + price += Encoder.GetPrice(Models[startIndex + m], bit); + m = (m << 1) | bit; + } + return price; + } + + public static void ReverseEncode(short[] Models, int startIndex, + Encoder rangeEncoder, int NumBitLevels, int symbol) throws IOException + { + int m = 1; + for (int i = 0; i < NumBitLevels; i++) + { + int bit = symbol & 1; + rangeEncoder.Encode(Models, startIndex + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java new file mode 100644 index 0000000..85b3150 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java @@ -0,0 +1,88 @@ +package SevenZip.Compression.RangeCoder; +import java.io.IOException; + +public class Decoder +{ + static final int kTopMask = ~((1 << 24) - 1); + + static final int kNumBitModelTotalBits = 11; + static final int kBitModelTotal = (1 << kNumBitModelTotalBits); + static final int kNumMoveBits = 5; + + int Range; + int Code; + + java.io.InputStream Stream; + + public final void SetStream(java.io.InputStream stream) + { + Stream = stream; + } + + public final void ReleaseStream() + { + Stream = null; + } + + public final void Init() throws IOException + { + Code = 0; + Range = -1; + for (int i = 0; i < 5; i++) + Code = (Code << 8) | Stream.read(); + } + + public final int DecodeDirectBits(int numTotalBits) throws IOException + { + int result = 0; + for (int i = numTotalBits; i != 0; i--) + { + Range >>>= 1; + int t = ((Code - Range) >>> 31); + Code -= Range & (t - 1); + result = (result << 1) | (1 - t); + + if ((Range & kTopMask) == 0) + { + Code = (Code << 8) | Stream.read(); + Range <<= 8; + } + } + return result; + } + + public int DecodeBit(short []probs, int index) throws IOException + { + int prob = probs[index]; + int newBound = (Range >>> kNumBitModelTotalBits) * prob; + if ((Code ^ 0x80000000) < (newBound ^ 0x80000000)) + { + Range = newBound; + probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits)); + if ((Range & kTopMask) == 0) + { + Code = (Code << 8) | Stream.read(); + Range <<= 8; + } + return 0; + } + else + { + Range -= newBound; + Code -= newBound; + probs[index] = (short)(prob - ((prob) >>> kNumMoveBits)); + if ((Range & kTopMask) == 0) + { + Code = (Code << 8) | Stream.read(); + Range <<= 8; + } + return 1; + } + } + + public static void InitBitModels(short []probs) + { + for (int i = 0; i < probs.length; i++) + probs[i] = (kBitModelTotal >>> 1); + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java new file mode 100644 index 0000000..d21b983 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java @@ -0,0 +1,151 @@ +package SevenZip.Compression.RangeCoder; +import java.io.IOException; + +public class Encoder +{ + static final int kTopMask = ~((1 << 24) - 1); + + static final int kNumBitModelTotalBits = 11; + static final int kBitModelTotal = (1 << kNumBitModelTotalBits); + static final int kNumMoveBits = 5; + + java.io.OutputStream Stream; + + long Low; + int Range; + int _cacheSize; + int _cache; + + long _position; + + public void SetStream(java.io.OutputStream stream) + { + Stream = stream; + } + + public void ReleaseStream() + { + Stream = null; + } + + public void Init() + { + _position = 0; + Low = 0; + Range = -1; + _cacheSize = 1; + _cache = 0; + } + + public void FlushData() throws IOException + { + for (int i = 0; i < 5; i++) + ShiftLow(); + } + + public void FlushStream() throws IOException + { + Stream.flush(); + } + + public void ShiftLow() throws IOException + { + int LowHi = (int)(Low >>> 32); + if (LowHi != 0 || Low < 0xFF000000L) + { + _position += _cacheSize; + int temp = _cache; + do + { + Stream.write(temp + LowHi); + temp = 0xFF; + } + while(--_cacheSize != 0); + _cache = (((int)Low) >>> 24); + } + _cacheSize++; + Low = (Low & 0xFFFFFF) << 8; + } + + public void EncodeDirectBits(int v, int numTotalBits) throws IOException + { + for (int i = numTotalBits - 1; i >= 0; i--) + { + Range >>>= 1; + if (((v >>> i) & 1) == 1) + Low += Range; + if ((Range & Encoder.kTopMask) == 0) + { + Range <<= 8; + ShiftLow(); + } + } + } + + + public long GetProcessedSizeAdd() + { + return _cacheSize + _position + 4; + } + + + + static final int kNumMoveReducingBits = 2; + public static final int kNumBitPriceShiftBits = 6; + + public static void InitBitModels(short []probs) + { + for (int i = 0; i < probs.length; i++) + probs[i] = (kBitModelTotal >>> 1); + } + + public void Encode(short []probs, int index, int symbol) throws IOException + { + int prob = probs[index]; + int newBound = (Range >>> kNumBitModelTotalBits) * prob; + if (symbol == 0) + { + Range = newBound; + probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits)); + } + else + { + Low += (newBound & 0xFFFFFFFFL); + Range -= newBound; + probs[index] = (short)(prob - ((prob) >>> kNumMoveBits)); + } + if ((Range & kTopMask) == 0) + { + Range <<= 8; + ShiftLow(); + } + } + + private static int[] ProbPrices = new int[kBitModelTotal >>> kNumMoveReducingBits]; + + static + { + int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + for (int i = kNumBits - 1; i >= 0; i--) + { + int start = 1 << (kNumBits - i - 1); + int end = 1 << (kNumBits - i); + for (int j = start; j < end; j++) + ProbPrices[j] = (i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1)); + } + } + + static public int GetPrice(int Prob, int symbol) + { + return ProbPrices[(((Prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits]; + } + static public int GetPrice0(int Prob) + { + return ProbPrices[Prob >>> kNumMoveReducingBits]; + } + static public int GetPrice1(int Prob) + { + return ProbPrices[(kBitModelTotal - Prob) >>> kNumMoveReducingBits]; + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/ICodeProgress.java b/3rdparty/physfs/lzma/Java/SevenZip/ICodeProgress.java new file mode 100644 index 0000000..4cb8d1b --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/ICodeProgress.java @@ -0,0 +1,6 @@ +package SevenZip; + +public interface ICodeProgress +{ + public void SetProgress(long inSize, long outSize); +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/LzmaAlone.java b/3rdparty/physfs/lzma/Java/SevenZip/LzmaAlone.java new file mode 100644 index 0000000..79c2b00 --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/LzmaAlone.java @@ -0,0 +1,253 @@ +package SevenZip; + +public class LzmaAlone +{ + static public class CommandLine + { + public static final int kEncode = 0; + public static final int kDecode = 1; + public static final int kBenchmak = 2; + + public int Command = -1; + public int NumBenchmarkPasses = 10; + + public int DictionarySize = 1 << 23; + public boolean DictionarySizeIsDefined = false; + + public int Lc = 3; + public int Lp = 0; + public int Pb = 2; + + public int Fb = 128; + public boolean FbIsDefined = false; + + public boolean Eos = false; + + public int Algorithm = 2; + public int MatchFinder = 1; + + public String InFile; + public String OutFile; + + boolean ParseSwitch(String s) + { + if (s.startsWith("d")) + { + DictionarySize = 1 << Integer.parseInt(s.substring(1)); + DictionarySizeIsDefined = true; + } + else if (s.startsWith("fb")) + { + Fb = Integer.parseInt(s.substring(2)); + FbIsDefined = true; + } + else if (s.startsWith("a")) + Algorithm = Integer.parseInt(s.substring(1)); + else if (s.startsWith("lc")) + Lc = Integer.parseInt(s.substring(2)); + else if (s.startsWith("lp")) + Lp = Integer.parseInt(s.substring(2)); + else if (s.startsWith("pb")) + Pb = Integer.parseInt(s.substring(2)); + else if (s.startsWith("eos")) + Eos = true; + else if (s.startsWith("mf")) + { + String mfs = s.substring(2); + if (mfs.equals("bt2")) + MatchFinder = 0; + else if (mfs.equals("bt4")) + MatchFinder = 1; + else if (mfs.equals("bt4b")) + MatchFinder = 2; + else + return false; + } + else + return false; + return true; + } + + public boolean Parse(String[] args) throws Exception + { + int pos = 0; + boolean switchMode = true; + for (int i = 0; i < args.length; i++) + { + String s = args[i]; + if (s.length() == 0) + return false; + if (switchMode) + { + if (s.compareTo("--") == 0) + { + switchMode = false; + continue; + } + if (s.charAt(0) == '-') + { + String sw = s.substring(1).toLowerCase(); + if (sw.length() == 0) + return false; + try + { + if (!ParseSwitch(sw)) + return false; + } + catch (NumberFormatException e) + { + return false; + } + continue; + } + } + if (pos == 0) + { + if (s.equalsIgnoreCase("e")) + Command = kEncode; + else if (s.equalsIgnoreCase("d")) + Command = kDecode; + else if (s.equalsIgnoreCase("b")) + Command = kBenchmak; + else + return false; + } + else if(pos == 1) + { + if (Command == kBenchmak) + { + try + { + NumBenchmarkPasses = Integer.parseInt(s); + if (NumBenchmarkPasses < 1) + return false; + } + catch (NumberFormatException e) + { + return false; + } + } + else + InFile = s; + } + else if(pos == 2) + OutFile = s; + else + return false; + pos++; + continue; + } + return true; + } + } + + + static void PrintHelp() + { + System.out.println( + "\nUsage: LZMA [...] inputFile outputFile\n" + + " e: encode file\n" + + " d: decode file\n" + + " b: Benchmark\n" + + "\n" + + // " -a{N}: set compression mode - [0, 1], default: 1 (max)\n" + + " -d{N}: set dictionary - [0,28], default: 23 (8MB)\n" + + " -fb{N}: set number of fast bytes - [5, 273], default: 128\n" + + " -lc{N}: set number of literal context bits - [0, 8], default: 3\n" + + " -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" + + " -pb{N}: set number of pos bits - [0, 4], default: 2\n" + + " -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" + + " -eos: write End Of Stream marker\n" + ); + } + + public static void main(String[] args) throws Exception + { + System.out.println("\nLZMA (Java) 4.42 Copyright (c) 1999-2006 Igor Pavlov 2006-05-15\n"); + + if (args.length < 1) + { + PrintHelp(); + return; + } + + CommandLine params = new CommandLine(); + if (!params.Parse(args)) + { + System.out.println("\nIncorrect command"); + return; + } + + if (params.Command == CommandLine.kBenchmak) + { + int dictionary = (1 << 21); + if (params.DictionarySizeIsDefined) + dictionary = params.DictionarySize; + if (params.MatchFinder > 1) + throw new Exception("Unsupported match finder"); + SevenZip.LzmaBench.LzmaBenchmark(params.NumBenchmarkPasses, dictionary); + } + else if (params.Command == CommandLine.kEncode || params.Command == CommandLine.kDecode) + { + java.io.File inFile = new java.io.File(params.InFile); + java.io.File outFile = new java.io.File(params.OutFile); + + java.io.BufferedInputStream inStream = new java.io.BufferedInputStream(new java.io.FileInputStream(inFile)); + java.io.BufferedOutputStream outStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outFile)); + + boolean eos = false; + if (params.Eos) + eos = true; + if (params.Command == CommandLine.kEncode) + { + SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder(); + if (!encoder.SetAlgorithm(params.Algorithm)) + throw new Exception("Incorrect compression mode"); + if (!encoder.SetDictionarySize(params.DictionarySize)) + throw new Exception("Incorrect dictionary size"); + if (!encoder.SeNumFastBytes(params.Fb)) + throw new Exception("Incorrect -fb value"); + if (!encoder.SetMatchFinder(params.MatchFinder)) + throw new Exception("Incorrect -mf value"); + if (!encoder.SetLcLpPb(params.Lc, params.Lp, params.Pb)) + throw new Exception("Incorrect -lc or -lp or -pb value"); + encoder.SetEndMarkerMode(eos); + encoder.WriteCoderProperties(outStream); + long fileSize; + if (eos) + fileSize = -1; + else + fileSize = inFile.length(); + for (int i = 0; i < 8; i++) + outStream.write((int)(fileSize >>> (8 * i)) & 0xFF); + encoder.Code(inStream, outStream, -1, -1, null); + } + else + { + int propertiesSize = 5; + byte[] properties = new byte[propertiesSize]; + if (inStream.read(properties, 0, propertiesSize) != propertiesSize) + throw new Exception("input .lzma file is too short"); + SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); + if (!decoder.SetDecoderProperties(properties)) + throw new Exception("Incorrect stream properties"); + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = inStream.read(); + if (v < 0) + throw new Exception("Can't read stream size"); + outSize |= ((long)v) << (8 * i); + } + if (!decoder.Code(inStream, outStream, outSize)) + throw new Exception("Error in data stream"); + } + outStream.flush(); + outStream.close(); + inStream.close(); + } + else + throw new Exception("Incorrect command"); + return; + } +} diff --git a/3rdparty/physfs/lzma/Java/SevenZip/LzmaBench.java b/3rdparty/physfs/lzma/Java/SevenZip/LzmaBench.java new file mode 100644 index 0000000..397e7af --- /dev/null +++ b/3rdparty/physfs/lzma/Java/SevenZip/LzmaBench.java @@ -0,0 +1,392 @@ +package SevenZip; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +public class LzmaBench +{ + static final int kAdditionalSize = (1 << 21); + static final int kCompressedAdditionalSize = (1 << 10); + + static class CRandomGenerator + { + int A1; + int A2; + public CRandomGenerator() { Init(); } + public void Init() { A1 = 362436069; A2 = 521288629; } + public int GetRnd() + { + return + ((A1 = 36969 * (A1 & 0xffff) + (A1 >>> 16)) << 16) ^ + ((A2 = 18000 * (A2 & 0xffff) + (A2 >>> 16))); + } + }; + + static class CBitRandomGenerator + { + CRandomGenerator RG = new CRandomGenerator(); + int Value; + int NumBits; + public void Init() + { + Value = 0; + NumBits = 0; + } + public int GetRnd(int numBits) + { + int result; + if (NumBits > numBits) + { + result = Value & ((1 << numBits) - 1); + Value >>>= numBits; + NumBits -= numBits; + return result; + } + numBits -= NumBits; + result = (Value << numBits); + Value = RG.GetRnd(); + result |= Value & (((int)1 << numBits) - 1); + Value >>>= numBits; + NumBits = 32 - numBits; + return result; + } + }; + + static class CBenchRandomGenerator + { + CBitRandomGenerator RG = new CBitRandomGenerator(); + int Pos; + int Rep0; + + public int BufferSize; + public byte[] Buffer = null; + + public CBenchRandomGenerator() { } + public void Set(int bufferSize) + { + Buffer = new byte[bufferSize]; + Pos = 0; + BufferSize = bufferSize; + } + int GetRndBit() { return RG.GetRnd(1); } + int GetLogRandBits(int numBits) + { + int len = RG.GetRnd(numBits); + return RG.GetRnd((int)len); + } + int GetOffset() + { + if (GetRndBit() == 0) + return GetLogRandBits(4); + return (GetLogRandBits(4) << 10) | RG.GetRnd(10); + } + int GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); } + int GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); } + public void Generate() + { + RG.Init(); + Rep0 = 1; + while (Pos < BufferSize) + { + if (GetRndBit() == 0 || Pos < 1) + Buffer[Pos++] = (byte)(RG.GetRnd(8)); + else + { + int len; + if (RG.GetRnd(3) == 0) + len = 1 + GetLen1(); + else + { + do + Rep0 = GetOffset(); + while (Rep0 >= Pos); + Rep0++; + len = 2 + GetLen2(); + } + for (int i = 0; i < len && Pos < BufferSize; i++, Pos++) + Buffer[Pos] = Buffer[Pos - Rep0]; + } + } + } + }; + + static class CrcOutStream extends java.io.OutputStream + { + public CRC CRC = new CRC(); + + public void Init() + { + CRC.Init(); + } + public int GetDigest() + { + return CRC.GetDigest(); + } + public void write(byte[] b) + { + CRC.Update(b); + } + public void write(byte[] b, int off, int len) + { + CRC.Update(b, off, len); + } + public void write(int b) + { + CRC.UpdateByte(b); + } + }; + + static class MyOutputStream extends java.io.OutputStream + { + byte[] _buffer; + int _size; + int _pos; + + public MyOutputStream(byte[] buffer) + { + _buffer = buffer; + _size = _buffer.length; + } + + public void reset() + { + _pos = 0; + } + + public void write(int b) throws IOException + { + if (_pos >= _size) + throw new IOException("Error"); + _buffer[_pos++] = (byte)b; + } + + public int size() + { + return _pos; + } + }; + + static class MyInputStream extends java.io.InputStream + { + byte[] _buffer; + int _size; + int _pos; + + public MyInputStream(byte[] buffer, int size) + { + _buffer = buffer; + _size = size; + } + + public void reset() + { + _pos = 0; + } + + public int read() + { + if (_pos >= _size) + return -1; + return _buffer[_pos++] & 0xFF; + } + }; + + static class CProgressInfo implements ICodeProgress + { + public long ApprovedStart; + public long InSize; + public long Time; + public void Init() + { InSize = 0; } + public void SetProgress(long inSize, long outSize) + { + if (inSize >= ApprovedStart && InSize == 0) + { + Time = System.currentTimeMillis(); + InSize = inSize; + } + } + } + static final int kSubBits = 8; + + static int GetLogSize(int size) + { + for (int i = kSubBits; i < 32; i++) + for (int j = 0; j < (1 << kSubBits); j++) + if (size <= ((1) << i) + (j << (i - kSubBits))) + return (i << kSubBits) + j; + return (32 << kSubBits); + } + + static long MyMultDiv64(long value, long elapsedTime) + { + long freq = 1000; // ms + long elTime = elapsedTime; + while (freq > 1000000) + { + freq >>>= 1; + elTime >>>= 1; + } + if (elTime == 0) + elTime = 1; + return value * freq / elTime; + } + + static long GetCompressRating(int dictionarySize, long elapsedTime, long size) + { + long t = GetLogSize(dictionarySize) - (18 << kSubBits); + long numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits)); + long numCommands = (long)(size) * numCommandsForOne; + return MyMultDiv64(numCommands, elapsedTime); + } + + static long GetDecompressRating(long elapsedTime, long outSize, long inSize) + { + long numCommands = inSize * 220 + outSize * 20; + return MyMultDiv64(numCommands, elapsedTime); + } + + static long GetTotalRating( + int dictionarySize, + long elapsedTimeEn, long sizeEn, + long elapsedTimeDe, + long inSizeDe, long outSizeDe) + { + return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) + + GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2; + } + + static void PrintValue(long v) + { + String s = ""; + s += v; + for (int i = 0; i + s.length() < 6; i++) + System.out.print(" "); + System.out.print(s); + } + + static void PrintRating(long rating) + { + PrintValue(rating / 1000000); + System.out.print(" MIPS"); + } + + static void PrintResults( + int dictionarySize, + long elapsedTime, + long size, + boolean decompressMode, long secondSize) + { + long speed = MyMultDiv64(size, elapsedTime); + PrintValue(speed / 1024); + System.out.print(" KB/s "); + long rating; + if (decompressMode) + rating = GetDecompressRating(elapsedTime, size, secondSize); + else + rating = GetCompressRating(dictionarySize, elapsedTime, size); + PrintRating(rating); + } + + static public int LzmaBenchmark(int numIterations, int dictionarySize) throws Exception + { + if (numIterations <= 0) + return 0; + if (dictionarySize < (1 << 18)) + { + System.out.println("\nError: dictionary size for benchmark must be >= 18 (256 KB)"); + return 1; + } + System.out.print("\n Compressing Decompressing\n\n"); + + SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder(); + SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); + + if (!encoder.SetDictionarySize(dictionarySize)) + throw new Exception("Incorrect dictionary size"); + + int kBufferSize = dictionarySize + kAdditionalSize; + int kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize; + + ByteArrayOutputStream propStream = new ByteArrayOutputStream(); + encoder.WriteCoderProperties(propStream); + byte[] propArray = propStream.toByteArray(); + decoder.SetDecoderProperties(propArray); + + CBenchRandomGenerator rg = new CBenchRandomGenerator(); + + rg.Set(kBufferSize); + rg.Generate(); + CRC crc = new CRC(); + crc.Init(); + crc.Update(rg.Buffer, 0, rg.BufferSize); + + CProgressInfo progressInfo = new CProgressInfo(); + progressInfo.ApprovedStart = dictionarySize; + + long totalBenchSize = 0; + long totalEncodeTime = 0; + long totalDecodeTime = 0; + long totalCompressedSize = 0; + + MyInputStream inStream = new MyInputStream(rg.Buffer, rg.BufferSize); + + byte[] compressedBuffer = new byte[kCompressedBufferSize]; + MyOutputStream compressedStream = new MyOutputStream(compressedBuffer); + CrcOutStream crcOutStream = new CrcOutStream(); + MyInputStream inputCompressedStream = null; + int compressedSize = 0; + for (int i = 0; i < numIterations; i++) + { + progressInfo.Init(); + inStream.reset(); + compressedStream.reset(); + encoder.Code(inStream, compressedStream, -1, -1, progressInfo); + long encodeTime = System.currentTimeMillis() - progressInfo.Time; + + if (i == 0) + { + compressedSize = compressedStream.size(); + inputCompressedStream = new MyInputStream(compressedBuffer, compressedSize); + } + else if (compressedSize != compressedStream.size()) + throw (new Exception("Encoding error")); + + if (progressInfo.InSize == 0) + throw (new Exception("Internal ERROR 1282")); + + long decodeTime = 0; + for (int j = 0; j < 2; j++) + { + inputCompressedStream.reset(); + crcOutStream.Init(); + + long outSize = kBufferSize; + long startTime = System.currentTimeMillis(); + if (!decoder.Code(inputCompressedStream, crcOutStream, outSize)) + throw (new Exception("Decoding Error"));; + decodeTime = System.currentTimeMillis() - startTime; + if (crcOutStream.GetDigest() != crc.GetDigest()) + throw (new Exception("CRC Error")); + } + long benchSize = kBufferSize - (long)progressInfo.InSize; + PrintResults(dictionarySize, encodeTime, benchSize, false, 0); + System.out.print(" "); + PrintResults(dictionarySize, decodeTime, kBufferSize, true, compressedSize); + System.out.println(); + + totalBenchSize += benchSize; + totalEncodeTime += encodeTime; + totalDecodeTime += decodeTime; + totalCompressedSize += compressedSize; + } + System.out.println("---------------------------------------------------"); + PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0); + System.out.print(" "); + PrintResults(dictionarySize, totalDecodeTime, + kBufferSize * (long)numIterations, true, totalCompressedSize); + System.out.println(" Average"); + return 0; + } +} diff --git a/3rdparty/physfs/lzma/LGPL.txt b/3rdparty/physfs/lzma/LGPL.txt new file mode 100644 index 0000000..4c38901 --- /dev/null +++ b/3rdparty/physfs/lzma/LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rdparty/physfs/lzma/Methods.txt b/3rdparty/physfs/lzma/Methods.txt new file mode 100644 index 0000000..e615669 --- /dev/null +++ b/3rdparty/physfs/lzma/Methods.txt @@ -0,0 +1,141 @@ +7-Zip method IDs (4.56) +----------------------- + +Each compression or crypto method in 7z has unique binary value (ID). +The length of ID in bytes is arbitrary but it can not exceed 63 bits (8 bytes). + +If you want to add some new ID, you have two ways: +1) Write request for allocating IDs to 7-zip developers. +2) Generate 8-bytes ID: + + 7F ZZ ZZ ZZ ZZ ZZ MM MM + + 7F - Prefix for random IDs (1 byte) + ZZ ZZ ZZ ZZ ZZ - Developer ID (5 bytes). Use real random bytes. + + MM MM - Method ID (2 bytes) + + You can notify 7-Zip developers about your Developer ID / Method ID. + + Note: Use new ID only if old codec can not decode data encoded with new version. + + +List of defined IDs +------------------- + +00 - Copy +01 - Reserved +02 - Common + 03 Swap + - 2 Swap2 + - 4 Swap4 + 04 Delta (subject to change) + +03 - 7z + 01 - LZMA + 01 - Version + + 03 - Branch + 01 - x86 + 03 - BCJ + 1B - BCJ2 + 02 - PPC + 05 - BC_PPC_B (Big Endian) + 03 - Alpha + 01 - BC_Alpha + 04 - IA64 + 01 - BC_IA64 + 05 - ARM + 01 - BC_ARM + 06 - M68 + 05 - BC_M68_B (Big Endian) + 07 - ARM Thumb + 01 - BC_ARMThumb + 08 - SPARC + 05 - BC_SPARC + + 04 - PPMD + 01 - Version + + 7F - + 01 - experimental methods. + + 80 - reserved for independent developers + + E0 - Random IDs + +04 - Misc + 00 - Reserved + 01 - Zip + 00 - Copy (not used). Use {00} instead + 01 - Shrink + 06 - Implode + 08 - Deflate + 09 - Deflate64 + 12 - BZip2 (not used). Use {04 02 02} instead + 02 - BZip + 02 - BZip2 + 03 - Rar + 01 - Rar15 + 02 - Rar20 + 03 - Rar29 + 04 - Arj + 01 - Arj (1,2,3) + 02 - Arj 4 + 05 - Z + 06 - Lzh + 07 - Reserved for 7z + 08 - Cab + 09 - NSIS + 01 - DeflateNSIS + 02 - BZip2NSIS + + +06 - Crypto + 00 - + 01 - AES + 0x - AES-128 + 4x - AES-192 + 8x - AES-256 + Cx - AES + + x0 - ECB + x1 - CBC + x2 - CFB + x3 - OFB + + 07 - Reserved + 0F - Reserved + + F0 - Misc Ciphers (Real Ciphers without hashing algo) + + F1 - Misc Ciphers (Combine) + 01 - Zip + 01 - Main Zip crypto algo + 03 - RAR + 02 - + 03 - Rar29 AES-128 + (modified SHA-1) + 07 - 7z + 01 - AES-256 + SHA-256 + +07 - Hash (subject to change) + 00 - + 01 - CRC + 02 - SHA-1 + 03 - SHA-256 + 04 - SHA-384 + 05 - SHA-512 + + F0 - Misc Hash + + F1 - Misc + 03 - RAR + 03 - Rar29 Password Hashing (modified SHA1) + 07 - 7z + 01 - SHA-256 Password Hashing + + + + +--- +End of document diff --git a/3rdparty/physfs/lzma/history.txt b/3rdparty/physfs/lzma/history.txt new file mode 100644 index 0000000..8db5c10 --- /dev/null +++ b/3rdparty/physfs/lzma/history.txt @@ -0,0 +1,198 @@ +HISTORY of the LZMA SDK +----------------------- + + 4.57 2007-12-12 + ------------------------- + - Speed optimizations in Ñ++ LZMA Decoder. + - Small changes for more compatibility with some C/C++ compilers. + + + 4.49 beta 2007-07-05 + ------------------------- + - .7z ANSI-C Decoder: + - now it supports BCJ and BCJ2 filters + - now it supports files larger than 4 GB. + - now it supports "Last Write Time" field for files. + - C++ code for .7z archives compressing/decompressing from 7-zip + was included to LZMA SDK. + + + 4.43 2006-06-04 + ------------------------- + - Small changes for more compatibility with some C/C++ compilers. + + + 4.42 2006-05-15 + ------------------------- + - Small changes in .h files in ANSI-C version. + + + 4.39 beta 2006-04-14 + ------------------------- + - Bug in versions 4.33b:4.38b was fixed: + C++ version of LZMA encoder could not correctly compress + files larger than 2 GB with HC4 match finder (-mfhc4). + + + 4.37 beta 2005-04-06 + ------------------------- + - Fixes in C++ code: code could no be compiled if _NO_EXCEPTIONS was defined. + + + 4.35 beta 2005-03-02 + ------------------------- + - Bug was fixed in C++ version of LZMA Decoder: + If encoded stream was corrupted, decoder could access memory + outside of allocated range. + + + 4.34 beta 2006-02-27 + ------------------------- + - Compressing speed and memory requirements for compressing were increased + - LZMA now can use only these match finders: HC4, BT2, BT3, BT4 + + + 4.32 2005-12-09 + ------------------------- + - Java version of LZMA SDK was included + + + 4.30 2005-11-20 + ------------------------- + - Compression ratio was improved in -a2 mode + - Speed optimizations for compressing in -a2 mode + - -fb switch now supports values up to 273 + - Bug in 7z_C (7zIn.c) was fixed: + It used Alloc/Free functions from different memory pools. + So if program used two memory pools, it worked incorrectly. + - 7z_C: .7z format supporting was improved + - LZMA# SDK (C#.NET version) was included + + + 4.27 (Updated) 2005-09-21 + ------------------------- + - Some GUIDs/interfaces in C++ were changed. + IStream.h: + ISequentialInStream::Read now works as old ReadPart + ISequentialOutStream::Write now works as old WritePart + + + 4.27 2005-08-07 + ------------------------- + - Bug in LzmaDecodeSize.c was fixed: + if _LZMA_IN_CB and _LZMA_OUT_READ were defined, + decompressing worked incorrectly. + + + 4.26 2005-08-05 + ------------------------- + - Fixes in 7z_C code and LzmaTest.c: + previous versions could work incorrectly, + if malloc(0) returns 0 + + + 4.23 2005-06-29 + ------------------------- + - Small fixes in C++ code + + + 4.22 2005-06-10 + ------------------------- + - Small fixes + + + 4.21 2005-06-08 + ------------------------- + - Interfaces for ANSI-C LZMA Decoder (LzmaDecode.c) were changed + - New additional version of ANSI-C LZMA Decoder with zlib-like interface: + - LzmaStateDecode.h + - LzmaStateDecode.c + - LzmaStateTest.c + - ANSI-C LZMA Decoder now can decompress files larger than 4 GB + + + 4.17 2005-04-18 + ------------------------- + - New example for RAM->RAM compressing/decompressing: + LZMA + BCJ (filter for x86 code): + - LzmaRam.h + - LzmaRam.cpp + - LzmaRamDecode.h + - LzmaRamDecode.c + - -f86 switch for lzma.exe + + + 4.16 2005-03-29 + ------------------------- + - Bug was fixed in LzmaDecode.c (ANSI-C LZMA Decoder): + If _LZMA_OUT_READ was defined, and if encoded stream was corrupted, + decoder could access memory outside of allocated range. + - Speed optimization of ANSI-C LZMA Decoder (now it's about 20% faster). + Old version of LZMA Decoder now is in file LzmaDecodeSize.c. + LzmaDecodeSize.c can provide slightly smaller code than LzmaDecode.c + - Small speed optimization in LZMA C++ code + - filter for SPARC's code was added + - Simplified version of .7z ANSI-C Decoder was included + + + 4.06 2004-09-05 + ------------------------- + - Bug in v4.05 was fixed: + LZMA-Encoder didn't release output stream in some cases. + + + 4.05 2004-08-25 + ------------------------- + - Source code of filters for x86, IA-64, ARM, ARM-Thumb + and PowerPC code was included to SDK + - Some internal minor changes + + + 4.04 2004-07-28 + ------------------------- + - More compatibility with some C++ compilers + + + 4.03 2004-06-18 + ------------------------- + - "Benchmark" command was added. It measures compressing + and decompressing speed and shows rating values. + Also it checks hardware errors. + + + 4.02 2004-06-10 + ------------------------- + - C++ LZMA Encoder/Decoder code now is more portable + and it can be compiled by GCC on Linux. + + + 4.01 2004-02-15 + ------------------------- + - Some detection of data corruption was enabled. + LzmaDecode.c / RangeDecoderReadByte + ..... + { + rd->ExtraBytes = 1; + return 0xFF; + } + + + 4.00 2004-02-13 + ------------------------- + - Original version of LZMA SDK + + + +HISTORY of the LZMA +------------------- + 2001-2007: Improvements to LZMA compressing/decompressing code, + keeping compatibility with original LZMA format + 1996-2001: Development of LZMA compression format + + Some milestones: + + 2001-08-30: LZMA compression was added to 7-Zip + 1999-01-02: First version of 7-Zip was released + + +End of document diff --git a/3rdparty/physfs/lzma/lzma.exe b/3rdparty/physfs/lzma/lzma.exe new file mode 100755 index 0000000..7239470 Binary files /dev/null and b/3rdparty/physfs/lzma/lzma.exe differ diff --git a/3rdparty/physfs/lzma/lzma.txt b/3rdparty/physfs/lzma/lzma.txt new file mode 100644 index 0000000..8169553 --- /dev/null +++ b/3rdparty/physfs/lzma/lzma.txt @@ -0,0 +1,663 @@ +LZMA SDK 4.57 +------------- + +LZMA SDK Copyright (C) 1999-2007 Igor Pavlov + +LZMA SDK provides the documentation, samples, header files, libraries, +and tools you need to develop applications that use LZMA compression. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + +LZMA is an improved version of famous LZ77 compression algorithm. +It was improved in way of maximum increasing of compression ratio, +keeping high decompression speed and low memory requirements for +decompressing. + + + +LICENSE +------- + +LZMA SDK is available under any of the following licenses: + +1) GNU Lesser General Public License (GNU LGPL) +2) Common Public License (CPL) +3) Simplified license for unmodified code (read SPECIAL EXCEPTION) +4) Proprietary license + +It means that you can select one of these four options and follow rules of that license. + + +1,2) GNU LGPL and CPL licenses are pretty similar and both these +licenses are classified as + - "Free software licenses" at http://www.gnu.org/ + - "OSI-approved" at http://www.opensource.org/ + + +3) SPECIAL EXCEPTION + +Igor Pavlov, as the author of this code, expressly permits you +to statically or dynamically link your code (or bind by name) +to the files from LZMA SDK without subjecting your linked +code to the terms of the CPL or GNU LGPL. +Any modifications or additions to files from LZMA SDK, however, +are subject to the GNU LGPL or CPL terms. + +SPECIAL EXCEPTION allows you to use LZMA SDK in applications with closed code, +while you keep LZMA SDK code unmodified. + + +SPECIAL EXCEPTION #2: Igor Pavlov, as the author of this code, expressly permits +you to use this code under the same terms and conditions contained in the License +Agreement you have for any previous version of LZMA SDK developed by Igor Pavlov. + +SPECIAL EXCEPTION #2 allows owners of proprietary licenses to use latest version +of LZMA SDK as update for previous versions. + + +SPECIAL EXCEPTION #3: Igor Pavlov, as the author of this code, expressly permits +you to use code of the following files: +BranchTypes.h, LzmaTypes.h, LzmaTest.c, LzmaStateTest.c, LzmaAlone.cpp, +LzmaAlone.cs, LzmaAlone.java +as public domain code. + + +4) Proprietary license + +LZMA SDK also can be available under a proprietary license which +can include: + +1) Right to modify code without subjecting modified code to the +terms of the CPL or GNU LGPL +2) Technical support for code + +To request such proprietary license or any additional consultations, +send email message from that page: +http://www.7-zip.org/support.html + + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You should have received a copy of the Common Public License +along with this library. + + +LZMA SDK Contents +----------------- + +LZMA SDK includes: + + - C++ source code of LZMA compressing and decompressing + - ANSI-C compatible source code for LZMA decompressing + - C# source code for LZMA compressing and decompressing + - Java source code for LZMA compressing and decompressing + - Compiled file->file LZMA compressing/decompressing program for Windows system + +ANSI-C LZMA decompression code was ported from original C++ sources to C. +Also it was simplified and optimized for code size. +But it is fully compatible with LZMA from 7-Zip. + + +UNIX/Linux version +------------------ +To compile C++ version of file->file LZMA, go to directory +C/7zip/Compress/LZMA_Alone +and type "make" or "make clean all" to recompile all. + +In some UNIX/Linux versions you must compile LZMA with static libraries. +To compile with static libraries, change string in makefile +LIB = -lm +to string +LIB = -lm -static + + +Files +--------------------- +C - C source code +CPP - CPP source code +CS - C# source code +Java - Java source code +lzma.txt - LZMA SDK description (this file) +7zFormat.txt - 7z Format description +7zC.txt - 7z ANSI-C Decoder description (this file) +methods.txt - Compression method IDs for .7z +LGPL.txt - GNU Lesser General Public License +CPL.html - Common Public License +lzma.exe - Compiled file->file LZMA encoder/decoder for Windows +history.txt - history of the LZMA SDK + + +Source code structure +--------------------- + +C - C files + Compress - files related to compression/decompression + Lz - files related to LZ (Lempel-Ziv) compression algorithm + Lzma - ANSI-C compatible LZMA decompressor + + LzmaDecode.h - interface for LZMA decoding on ANSI-C + LzmaDecode.c - LZMA decoding on ANSI-C (new fastest version) + LzmaDecodeSize.c - LZMA decoding on ANSI-C (old size-optimized version) + LzmaTest.c - test application that decodes LZMA encoded file + LzmaTypes.h - basic types for LZMA Decoder + LzmaStateDecode.h - interface for LZMA decoding (State version) + LzmaStateDecode.c - LZMA decoding on ANSI-C (State version) + LzmaStateTest.c - test application (State version) + + Branch - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code + + Archive - files related to archiving + 7z_C - 7z ANSI-C Decoder + + +CPP -- CPP files + + Common - common files for C++ projects + Windows - common files for Windows related code + 7zip - files related to 7-Zip Project + + Common - common files for 7-Zip + + Compress - files related to compression/decompression + + LZ - files related to LZ (Lempel-Ziv) compression algorithm + + Copy - Copy coder + RangeCoder - Range Coder (special code of compression/decompression) + LZMA - LZMA compression/decompression on C++ + LZMA_Alone - file->file LZMA compression/decompression + + Branch - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code + + Archive - files related to archiving + + Common - common files for archive handling + 7z - 7z C++ Encoder/Decoder + + Bundles - Modules that are bundles of other modules + + Alone7z - 7zr.exe: Standalone version of 7z.exe that supports only 7z/LZMA/BCJ/BCJ2 + Format7zR - 7zr.dll: Reduced version of 7za.dll: extracting/compressing to 7z/LZMA/BCJ/BCJ2 + Format7zExtractR - 7zxr.dll: Reduced version of 7zxa.dll: extracting from 7z/LZMA/BCJ/BCJ2. + + UI - User Interface files + + Client7z - Test application for 7za.dll, 7zr.dll, 7zxr.dll + Common - Common UI files + Console - Code for console archiver + + + +CS - C# files + 7zip + Common - some common files for 7-Zip + Compress - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + LzmaAlone - file->file LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + +Java - Java files + SevenZip + Compression - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + +C/C++ source code of LZMA SDK is part of 7-Zip project. + +You can find ANSI-C LZMA decompressing code at folder + C/7zip/Compress/Lzma +7-Zip doesn't use that ANSI-C LZMA code and that code was developed +specially for this SDK. And files from C/7zip/Compress/Lzma do not need +files from other directories of SDK for compiling. + +7-Zip source code can be downloaded from 7-Zip's SourceForge page: + + http://sourceforge.net/projects/sevenzip/ + + +LZMA features +------------- + - Variable dictionary size (up to 1 GB) + - Estimated compressing speed: about 1 MB/s on 1 GHz CPU + - Estimated decompressing speed: + - 8-12 MB/s on 1 GHz Intel Pentium 3 or AMD Athlon + - 500-1000 KB/s on 100 MHz ARM, MIPS, PowerPC or other simple RISC + - Small memory requirements for decompressing (8-32 KB + DictionarySize) + - Small code size for decompressing: 2-8 KB (depending from + speed optimizations) + +LZMA decoder uses only integer operations and can be +implemented in any modern 32-bit CPU (or on 16-bit CPU with some conditions). + +Some critical operations that affect to speed of LZMA decompression: + 1) 32*16 bit integer multiply + 2) Misspredicted branches (penalty mostly depends from pipeline length) + 3) 32-bit shift and arithmetic operations + +Speed of LZMA decompressing mostly depends from CPU speed. +Memory speed has no big meaning. But if your CPU has small data cache, +overall weight of memory speed will slightly increase. + + +How To Use +---------- + +Using LZMA encoder/decoder executable +-------------------------------------- + +Usage: LZMA inputFile outputFile [...] + + e: encode file + + d: decode file + + b: Benchmark. There are two tests: compressing and decompressing + with LZMA method. Benchmark shows rating in MIPS (million + instructions per second). Rating value is calculated from + measured speed and it is normalized with AMD Athlon 64 X2 CPU + results. Also Benchmark checks possible hardware errors (RAM + errors in most cases). Benchmark uses these settings: + (-a1, -d21, -fb32, -mfbt4). You can change only -d. Also you + can change number of iterations. Example for 30 iterations: + LZMA b 30 + Default number of iterations is 10. + + + + + -a{N}: set compression mode 0 = fast, 1 = normal + default: 1 (normal) + + d{N}: Sets Dictionary size - [0, 30], default: 23 (8MB) + The maximum value for dictionary size is 1 GB = 2^30 bytes. + Dictionary size is calculated as DictionarySize = 2^N bytes. + For decompressing file compressed by LZMA method with dictionary + size D = 2^N you need about D bytes of memory (RAM). + + -fb{N}: set number of fast bytes - [5, 273], default: 128 + Usually big number gives a little bit better compression ratio + and slower compression process. + + -lc{N}: set number of literal context bits - [0, 8], default: 3 + Sometimes lc=4 gives gain for big files. + + -lp{N}: set number of literal pos bits - [0, 4], default: 0 + lp switch is intended for periodical data when period is + equal 2^N. For example, for 32-bit (4 bytes) + periodical data you can use lp=2. Often it's better to set lc0, + if you change lp switch. + + -pb{N}: set number of pos bits - [0, 4], default: 2 + pb switch is intended for periodical data + when period is equal 2^N. + + -mf{MF_ID}: set Match Finder. Default: bt4. + Algorithms from hc* group doesn't provide good compression + ratio, but they often works pretty fast in combination with + fast mode (-a0). + + Memory requirements depend from dictionary size + (parameter "d" in table below). + + MF_ID Memory Description + + bt2 d * 9.5 + 4MB Binary Tree with 2 bytes hashing. + bt3 d * 11.5 + 4MB Binary Tree with 3 bytes hashing. + bt4 d * 11.5 + 4MB Binary Tree with 4 bytes hashing. + hc4 d * 7.5 + 4MB Hash Chain with 4 bytes hashing. + + -eos: write End Of Stream marker. By default LZMA doesn't write + eos marker, since LZMA decoder knows uncompressed size + stored in .lzma file header. + + -si: Read data from stdin (it will write End Of Stream marker). + -so: Write data to stdout + + +Examples: + +1) LZMA e file.bin file.lzma -d16 -lc0 + +compresses file.bin to file.lzma with 64 KB dictionary (2^16=64K) +and 0 literal context bits. -lc0 allows to reduce memory requirements +for decompression. + + +2) LZMA e file.bin file.lzma -lc0 -lp2 + +compresses file.bin to file.lzma with settings suitable +for 32-bit periodical data (for example, ARM or MIPS code). + +3) LZMA d file.lzma file.bin + +decompresses file.lzma to file.bin. + + +Compression ratio hints +----------------------- + +Recommendations +--------------- + +To increase compression ratio for LZMA compressing it's desirable +to have aligned data (if it's possible) and also it's desirable to locate +data in such order, where code is grouped in one place and data is +grouped in other place (it's better than such mixing: code, data, code, +data, ...). + + +Using Filters +------------- +You can increase compression ratio for some data types, using +special filters before compressing. For example, it's possible to +increase compression ratio on 5-10% for code for those CPU ISAs: +x86, IA-64, ARM, ARM-Thumb, PowerPC, SPARC. + +You can find C/C++ source code of such filters in folder "7zip/Compress/Branch" + +You can check compression ratio gain of these filters with such +7-Zip commands (example for ARM code): +No filter: + 7z a a1.7z a.bin -m0=lzma + +With filter for little-endian ARM code: + 7z a a2.7z a.bin -m0=bc_arm -m1=lzma + +With filter for big-endian ARM code (using additional Swap4 filter): + 7z a a3.7z a.bin -m0=swap4 -m1=bc_arm -m2=lzma + +It works in such manner: +Compressing = Filter_encoding + LZMA_encoding +Decompressing = LZMA_decoding + Filter_decoding + +Compressing and decompressing speed of such filters is very high, +so it will not increase decompressing time too much. +Moreover, it reduces decompression time for LZMA_decoding, +since compression ratio with filtering is higher. + +These filters convert CALL (calling procedure) instructions +from relative offsets to absolute addresses, so such data becomes more +compressible. Source code of these CALL filters is pretty simple +(about 20 lines of C++), so you can convert it from C++ version yourself. + +For some ISAs (for example, for MIPS) it's impossible to get gain from such filter. + + +LZMA compressed file format +--------------------------- +Offset Size Description + 0 1 Special LZMA properties for compressed data + 1 4 Dictionary size (little endian) + 5 8 Uncompressed size (little endian). -1 means unknown size + 13 Compressed data + + +ANSI-C LZMA Decoder +~~~~~~~~~~~~~~~~~~~ + +To compile ANSI-C LZMA Decoder you can use one of the following files sets: +1) LzmaDecode.h + LzmaDecode.c + LzmaTest.c (fastest version) +2) LzmaDecode.h + LzmaDecodeSize.c + LzmaTest.c (old size-optimized version) +3) LzmaStateDecode.h + LzmaStateDecode.c + LzmaStateTest.c (zlib-like interface) + + +Memory requirements for LZMA decoding +------------------------------------- + +LZMA decoder doesn't allocate memory itself, so you must +allocate memory and send it to LZMA. + +Stack usage of LZMA decoding function for local variables is not +larger than 200 bytes. + +How To decompress data +---------------------- + +LZMA Decoder (ANSI-C version) now supports 5 interfaces: +1) Single-call Decompressing +2) Single-call Decompressing with input stream callback +3) Multi-call Decompressing with output buffer +4) Multi-call Decompressing with input callback and output buffer +5) Multi-call State Decompressing (zlib-like interface) + +Variant-5 is similar to Variant-4, but Variant-5 doesn't use callback functions. + +Decompressing steps +------------------- + +1) read LZMA properties (5 bytes): + unsigned char properties[LZMA_PROPERTIES_SIZE]; + +2) read uncompressed size (8 bytes, little-endian) + +3) Decode properties: + + CLzmaDecoderState state; /* it's 24-140 bytes structure, if int is 32-bit */ + + if (LzmaDecodeProperties(&state.Properties, properties, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK) + return PrintError(rs, "Incorrect stream properties"); + +4) Allocate memory block for internal Structures: + + state.Probs = (CProb *)malloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (state.Probs == 0) + return PrintError(rs, kCantAllocateMessage); + + LZMA decoder uses array of CProb variables as internal structure. + By default, CProb is unsigned_short. But you can define _LZMA_PROB32 to make + it unsigned_int. It can increase speed on some 32-bit CPUs, but memory + usage will be doubled in that case. + + +5) Main Decompressing + +You must use one of the following interfaces: + +5.1 Single-call Decompressing +----------------------------- +When to use: RAM->RAM decompressing +Compile files: LzmaDecode.h, LzmaDecode.c +Compile defines: no defines +Memory Requirements: + - Input buffer: compressed size + - Output buffer: uncompressed size + - LZMA Internal Structures (~16 KB for default settings) + +Interface: + int res = LzmaDecode(&state, + inStream, compressedSize, &inProcessed, + outStream, outSize, &outProcessed); + + +5.2 Single-call Decompressing with input stream callback +-------------------------------------------------------- +When to use: File->RAM or Flash->RAM decompressing. +Compile files: LzmaDecode.h, LzmaDecode.c +Compile defines: _LZMA_IN_CB +Memory Requirements: + - Buffer for input stream: any size (for example, 16 KB) + - Output buffer: uncompressed size + - LZMA Internal Structures (~16 KB for default settings) + +Interface: + typedef struct _CBuffer + { + ILzmaInCallback InCallback; + FILE *File; + unsigned char Buffer[kInBufferSize]; + } CBuffer; + + int LzmaReadCompressed(void *object, const unsigned char **buffer, SizeT *size) + { + CBuffer *bo = (CBuffer *)object; + *buffer = bo->Buffer; + *size = MyReadFile(bo->File, bo->Buffer, kInBufferSize); + return LZMA_RESULT_OK; + } + + CBuffer g_InBuffer; + + g_InBuffer.File = inFile; + g_InBuffer.InCallback.Read = LzmaReadCompressed; + int res = LzmaDecode(&state, + &g_InBuffer.InCallback, + outStream, outSize, &outProcessed); + + +5.3 Multi-call decompressing with output buffer +----------------------------------------------- +When to use: RAM->File decompressing +Compile files: LzmaDecode.h, LzmaDecode.c +Compile defines: _LZMA_OUT_READ +Memory Requirements: + - Input buffer: compressed size + - Buffer for output stream: any size (for example, 16 KB) + - LZMA Internal Structures (~16 KB for default settings) + - LZMA dictionary (dictionary size is encoded in stream properties) + +Interface: + + state.Dictionary = (unsigned char *)malloc(state.Properties.DictionarySize); + + LzmaDecoderInit(&state); + do + { + LzmaDecode(&state, + inBuffer, inAvail, &inProcessed, + g_OutBuffer, outAvail, &outProcessed); + inAvail -= inProcessed; + inBuffer += inProcessed; + } + while you need more bytes + + see LzmaTest.c for more details. + + +5.4 Multi-call decompressing with input callback and output buffer +------------------------------------------------------------------ +When to use: File->File decompressing +Compile files: LzmaDecode.h, LzmaDecode.c +Compile defines: _LZMA_IN_CB, _LZMA_OUT_READ +Memory Requirements: + - Buffer for input stream: any size (for example, 16 KB) + - Buffer for output stream: any size (for example, 16 KB) + - LZMA Internal Structures (~16 KB for default settings) + - LZMA dictionary (dictionary size is encoded in stream properties) + +Interface: + + state.Dictionary = (unsigned char *)malloc(state.Properties.DictionarySize); + + LzmaDecoderInit(&state); + do + { + LzmaDecode(&state, + &bo.InCallback, + g_OutBuffer, outAvail, &outProcessed); + } + while you need more bytes + + see LzmaTest.c for more details: + + +5.5 Multi-call State Decompressing (zlib-like interface) +------------------------------------------------------------------ +When to use: file->file decompressing +Compile files: LzmaStateDecode.h, LzmaStateDecode.c +Compile defines: +Memory Requirements: + - Buffer for input stream: any size (for example, 16 KB) + - Buffer for output stream: any size (for example, 16 KB) + - LZMA Internal Structures (~16 KB for default settings) + - LZMA dictionary (dictionary size is encoded in stream properties) + +Interface: + + state.Dictionary = (unsigned char *)malloc(state.Properties.DictionarySize); + + + LzmaDecoderInit(&state); + do + { + res = LzmaDecode(&state, + inBuffer, inAvail, &inProcessed, + g_OutBuffer, outAvail, &outProcessed, + finishDecoding); + inAvail -= inProcessed; + inBuffer += inProcessed; + } + while you need more bytes + + see LzmaStateTest.c for more details: + + +6) Free all allocated blocks + + +Note +---- +LzmaDecodeSize.c is size-optimized version of LzmaDecode.c. +But compiled code of LzmaDecodeSize.c can be larger than +compiled code of LzmaDecode.c. So it's better to use +LzmaDecode.c in most cases. + + +EXIT codes +----------- + +LZMA decoder can return one of the following codes: + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + +If you use callback function for input data and you return some +error code, LZMA Decoder also returns that code. + + + +LZMA Defines +------------ + +_LZMA_IN_CB - Use callback for input data + +_LZMA_OUT_READ - Use read function for output data + +_LZMA_LOC_OPT - Enable local speed optimizations inside code. + _LZMA_LOC_OPT is only for LzmaDecodeSize.c (size-optimized version). + _LZMA_LOC_OPT doesn't affect LzmaDecode.c (speed-optimized version) + and LzmaStateDecode.c + +_LZMA_PROB32 - It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case + +_LZMA_UINT32_IS_ULONG - Define it if int is 16-bit on your compiler + and long is 32-bit. + +_LZMA_SYSTEM_SIZE_T - Define it if you want to use system's size_t. + You can use it to enable 64-bit sizes supporting + + + +C++ LZMA Encoder/Decoder +~~~~~~~~~~~~~~~~~~~~~~~~ +C++ LZMA code use COM-like interfaces. So if you want to use it, +you can study basics of COM/OLE. + +By default, LZMA Encoder contains all Match Finders. +But for compressing it's enough to have just one of them. +So for reducing size of compressing code you can define: + #define COMPRESS_MF_BT + #define COMPRESS_MF_BT4 +and it will use only bt4 match finder. + + +--- + +http://www.7-zip.org +http://www.7-zip.org/support.html diff --git a/3rdparty/physfs/physfs.c b/3rdparty/physfs/physfs.c new file mode 100644 index 0000000..580b5a1 --- /dev/null +++ b/3rdparty/physfs/physfs.c @@ -0,0 +1,2222 @@ +/** + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * Documentation is in physfs.h. It's verbose, honest. :) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + + +typedef struct __PHYSFS_DIRHANDLE__ +{ + void *opaque; /* Instance data unique to the archiver. */ + char *dirName; /* Path to archive in platform-dependent notation. */ + char *mountPoint; /* Mountpoint in virtual file tree. */ + const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */ + struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */ +} DirHandle; + + +typedef struct __PHYSFS_FILEHANDLE__ +{ + void *opaque; /* Instance data unique to the archiver for this file. */ + PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */ + const DirHandle *dirHandle; /* Archiver instance that created this */ + const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */ + PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */ + PHYSFS_uint32 bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */ + PHYSFS_uint32 buffill; /* Buffer fill size. Don't touch! */ + PHYSFS_uint32 bufpos; /* Buffer position. Don't touch! */ + struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */ +} FileHandle; + + +typedef struct __PHYSFS_ERRMSGTYPE__ +{ + void *tid; + int errorAvailable; + char errorString[80]; + struct __PHYSFS_ERRMSGTYPE__ *next; +} ErrMsg; + + +/* The various i/o drivers...some of these may not be compiled in. */ +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA; +extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK; +extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG; +extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL; +extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD; +extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD; +extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR; + + +static const PHYSFS_ArchiveInfo *supported_types[] = +{ +#if (defined PHYSFS_SUPPORTS_ZIP) + &__PHYSFS_ArchiveInfo_ZIP, +#endif +#if (defined PHYSFS_SUPPORTS_7Z) + &__PHYSFS_ArchiveInfo_LZMA, +#endif +#if (defined PHYSFS_SUPPORTS_GRP) + &__PHYSFS_ArchiveInfo_GRP, +#endif +#if (defined PHYSFS_SUPPORTS_QPAK) + &__PHYSFS_ArchiveInfo_QPAK, +#endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_ArchiveInfo_HOG, +#endif +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_ArchiveInfo_MVL, +#endif +#if (defined PHYSFS_SUPPORTS_WAD) + &__PHYSFS_ArchiveInfo_WAD, +#endif + NULL +}; + +static const PHYSFS_Archiver * const archivers[] = +{ + &__PHYSFS_Archiver_DIR, +#if (defined PHYSFS_SUPPORTS_ZIP) + &__PHYSFS_Archiver_ZIP, +#endif +#if (defined PHYSFS_SUPPORTS_7Z) + &__PHYSFS_Archiver_LZMA, +#endif +#if (defined PHYSFS_SUPPORTS_GRP) + &__PHYSFS_Archiver_GRP, +#endif +#if (defined PHYSFS_SUPPORTS_QPAK) + &__PHYSFS_Archiver_QPAK, +#endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_Archiver_HOG, +#endif +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_Archiver_MVL, +#endif +#if (defined PHYSFS_SUPPORTS_WAD) + &__PHYSFS_Archiver_WAD, +#endif + NULL +}; + + + +/* General PhysicsFS state ... */ +static int initialized = 0; +static ErrMsg *errorMessages = NULL; +static DirHandle *searchPath = NULL; +static DirHandle *writeDir = NULL; +static FileHandle *openWriteList = NULL; +static FileHandle *openReadList = NULL; +static char *baseDir = NULL; +static char *userDir = NULL; +static int allowSymLinks = 0; + +/* mutexes ... */ +static void *errorLock = NULL; /* protects error message list. */ +static void *stateLock = NULL; /* protects other PhysFS static state. */ + +/* allocator ... */ +static int externalAllocator = 0; +PHYSFS_Allocator allocator; + + +/* functions ... */ + +typedef struct +{ + char **list; + PHYSFS_uint32 size; + const char *errorstr; +} EnumStringListCallbackData; + +static void enumStringListCallback(void *data, const char *str) +{ + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + if (pecd->errorstr) + return; + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + { + pecd->errorstr = ERR_OUT_OF_MEMORY; + pecd->list[pecd->size] = NULL; + PHYSFS_freeList(pecd->list); + return; + } /* if */ + + strcpy(newstr, str); + pecd->list[pecd->size] = newstr; + pecd->size++; +} /* enumStringListCallback */ + + +static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *)) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL); + func(enumStringListCallback, &ecd); + BAIL_IF_MACRO(ecd.errorstr != NULL, ecd.errorstr, NULL); + ecd.list[ecd.size] = NULL; + return(ecd.list); +} /* doEnumStringList */ + + +static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + PHYSFS_uint32 i; + int sorted; + + do + { + sorted = 1; + for (i = lo; i < hi; i++) + { + if (cmpfn(a, i, i + 1) > 0) + { + swapfn(a, i, i + 1); + sorted = 0; + } /* if */ + } /* for */ + } while (!sorted); +} /* __PHYSFS_bubble_sort */ + + +static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + PHYSFS_uint32 i; + PHYSFS_uint32 j; + PHYSFS_uint32 v; + + if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD) + __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn); + else + { + i = (hi + lo) / 2; + + if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i); + if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi); + if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi); + + j = hi - 1; + swapfn(a, i, j); + i = lo; + v = j; + while (1) + { + while(cmpfn(a, ++i, v) < 0) { /* do nothing */ } + while(cmpfn(a, --j, v) > 0) { /* do nothing */ } + if (j < i) + break; + swapfn(a, i, j); + } /* while */ + if (i != (hi-1)) + swapfn(a, i, hi-1); + __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn); + __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn); + } /* else */ +} /* __PHYSFS_quick_sort */ + + +void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + /* + * Quicksort w/ Bubblesort fallback algorithm inspired by code from here: + * http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html + */ + if (max > 0) + __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn); +} /* __PHYSFS_sort */ + + +static ErrMsg *findErrorForCurrentThread(void) +{ + ErrMsg *i; + void *tid; + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + if (errorMessages != NULL) + { + tid = __PHYSFS_platformGetThreadID(); + + for (i = errorMessages; i != NULL; i = i->next) + { + if (i->tid == tid) + { + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + return(i); + } /* if */ + } /* for */ + } /* if */ + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + + return(NULL); /* no error available. */ +} /* findErrorForCurrentThread */ + + +void __PHYSFS_setError(const char *str) +{ + ErrMsg *err; + + if (str == NULL) + return; + + err = findErrorForCurrentThread(); + + if (err == NULL) + { + err = (ErrMsg *) allocator.Malloc(sizeof (ErrMsg)); + if (err == NULL) + return; /* uhh...? */ + + memset((void *) err, '\0', sizeof (ErrMsg)); + err->tid = __PHYSFS_platformGetThreadID(); + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + err->next = errorMessages; + errorMessages = err; + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + } /* if */ + + err->errorAvailable = 1; + strncpy(err->errorString, str, sizeof (err->errorString)); + err->errorString[sizeof (err->errorString) - 1] = '\0'; +} /* __PHYSFS_setError */ + + +const char *PHYSFS_getLastError(void) +{ + ErrMsg *err = findErrorForCurrentThread(); + + if ((err == NULL) || (!err->errorAvailable)) + return(NULL); + + err->errorAvailable = 0; + return(err->errorString); +} /* PHYSFS_getLastError */ + + +/* MAKE SURE that errorLock is held before calling this! */ +static void freeErrorMessages(void) +{ + ErrMsg *i; + ErrMsg *next; + + for (i = errorMessages; i != NULL; i = next) + { + next = i->next; + allocator.Free(i); + } /* for */ + + errorMessages = NULL; +} /* freeErrorMessages */ + + +void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) +{ + if (ver != NULL) + { + ver->major = PHYSFS_VER_MAJOR; + ver->minor = PHYSFS_VER_MINOR; + ver->patch = PHYSFS_VER_PATCH; + } /* if */ +} /* PHYSFS_getLinkedVersion */ + + +static const char *find_filename_extension(const char *fname) +{ + const char *retval = strchr(fname, '.'); + const char *p = retval; + + while (p != NULL) + { + p = strchr(p + 1, '.'); + if (p != NULL) + retval = p; + } /* while */ + + if (retval != NULL) + retval++; /* skip '.' */ + + return(retval); +} /* find_filename_extension */ + + +static DirHandle *tryOpenDir(const PHYSFS_Archiver *funcs, + const char *d, int forWriting) +{ + DirHandle *retval = NULL; + if (funcs->isArchive(d, forWriting)) + { + void *opaque = funcs->openArchive(d, forWriting); + if (opaque != NULL) + { + retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle)); + if (retval == NULL) + funcs->dirClose(opaque); + else + { + memset(retval, '\0', sizeof (DirHandle)); + retval->mountPoint = NULL; + retval->funcs = funcs; + retval->opaque = opaque; + } /* else */ + } /* if */ + } /* if */ + + return(retval); +} /* tryOpenDir */ + + +static DirHandle *openDirectory(const char *d, int forWriting) +{ + DirHandle *retval = NULL; + const PHYSFS_Archiver * const *i; + const char *ext; + + BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL); + + ext = find_filename_extension(d); + if (ext != NULL) + { + /* Look for archivers with matching file extensions first... */ + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + { + if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0) + retval = tryOpenDir(*i, d, forWriting); + } /* for */ + + /* failing an exact file extension match, try all the others... */ + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + { + if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0) + retval = tryOpenDir(*i, d, forWriting); + } /* for */ + } /* if */ + + else /* no extension? Try them all. */ + { + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + retval = tryOpenDir(*i, d, forWriting); + } /* else */ + + BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL); + return(retval); +} /* openDirectory */ + + +/* + * Make a platform-independent path string sane. Doesn't actually check the + * file hierarchy, it just cleans up the string. + * (dst) must be a buffer at least as big as (src), as this is where the + * cleaned up string is deposited. + * If there are illegal bits in the path (".." entries, etc) then we + * return zero and (dst) is undefined. Non-zero if the path was sanitized. + */ +static int sanitizePlatformIndependentPath(const char *src, char *dst) +{ + char *prev; + char ch; + + while (*src == '/') /* skip initial '/' chars... */ + src++; + + prev = dst; + do + { + ch = *(src++); + + if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */ + BAIL_MACRO(ERR_INSECURE_FNAME, 0); + + if (ch == '/') /* path separator. */ + { + *dst = '\0'; /* "." and ".." are illegal pathnames. */ + if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0)) + BAIL_MACRO(ERR_INSECURE_FNAME, 0); + + while (*src == '/') /* chop out doubles... */ + src++; + + if (*src == '\0') /* ends with a pathsep? */ + break; /* we're done, don't add final pathsep to dst. */ + + prev = dst + 1; + } /* if */ + + *(dst++) = ch; + } while (ch != '\0'); + + return(1); +} /* sanitizePlatformIndependentPath */ + + +/* + * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an + * output from sanitizePlatformIndependentPath(), so that it is in a known + * state. + * + * This only finds legitimate segments of a mountpoint. If the mountpoint is + * "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are + * all zero. "/a/b" will succeed, though. + */ +static int partOfMountPoint(DirHandle *h, char *fname) +{ + /* !!! FIXME: This code feels gross. */ + int rc; + size_t len, mntpntlen; + + if (h->mountPoint == NULL) + return(0); + else if (*fname == '\0') + return(1); + + len = strlen(fname); + mntpntlen = strlen(h->mountPoint); + if (len > mntpntlen) /* can't be a subset of mountpoint. */ + return(0); + + /* if true, must be not a match or a complete match, but not a subset. */ + if ((len + 1) == mntpntlen) + return(0); + + rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */ + if (rc != 0) + return(0); /* not a match. */ + + /* make sure /a/b matches /a/b/ and not /a/bc ... */ + return(h->mountPoint[len] == '/'); +} /* partOfMountPoint */ + + +static DirHandle *createDirHandle(const char *newDir, + const char *mountPoint, + int forWriting) +{ + DirHandle *dirHandle = NULL; + char *tmpmntpnt = NULL; + + GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle); + if (mountPoint != NULL) + { + const size_t len = strlen(mountPoint) + 1; + tmpmntpnt = (char *) __PHYSFS_smallAlloc(len); + GOTO_IF_MACRO(!tmpmntpnt, ERR_OUT_OF_MEMORY, badDirHandle); + if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt)) + goto badDirHandle; + mountPoint = tmpmntpnt; /* sanitized version. */ + } /* if */ + + dirHandle = openDirectory(newDir, forWriting); + GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle); + + dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1); + GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->dirName, newDir); + + if ((mountPoint != NULL) && (*mountPoint != '\0')) + { + dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2); + GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->mountPoint, mountPoint); + strcat(dirHandle->mountPoint, "/"); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return(dirHandle); + +badDirHandle: + if (dirHandle != NULL) + { + dirHandle->funcs->dirClose(dirHandle->opaque); + allocator.Free(dirHandle->dirName); + allocator.Free(dirHandle->mountPoint); + allocator.Free(dirHandle); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return(NULL); +} /* createDirHandle */ + + +/* MAKE SURE you've got the stateLock held before calling this! */ +static int freeDirHandle(DirHandle *dh, FileHandle *openList) +{ + FileHandle *i; + + if (dh == NULL) + return(1); + + for (i = openList; i != NULL; i = i->next) + BAIL_IF_MACRO(i->dirHandle == dh, ERR_FILES_STILL_OPEN, 0); + + dh->funcs->dirClose(dh->opaque); + allocator.Free(dh->dirName); + allocator.Free(dh->mountPoint); + allocator.Free(dh); + return(1); +} /* freeDirHandle */ + + +static char *calculateUserDir(void) +{ + char *retval = __PHYSFS_platformGetUserDir(); + if (retval != NULL) + { + /* make sure it really exists and is normalized. */ + char *ptr = __PHYSFS_platformRealPath(retval); + allocator.Free(retval); + retval = ptr; + } /* if */ + + if (retval == NULL) + { + const char *dirsep = PHYSFS_getDirSeparator(); + const char *uname = __PHYSFS_platformGetUserName(); + const char *str = (uname != NULL) ? uname : "default"; + + retval = (char *) allocator.Malloc(strlen(baseDir) + strlen(str) + + strlen(dirsep) + 6); + + if (retval == NULL) + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + else + sprintf(retval, "%susers%s%s", baseDir, dirsep, str); + + allocator.Free((void *) uname); + } /* else */ + + return(retval); +} /* calculateUserDir */ + + +static int appendDirSep(char **dir) +{ + const char *dirsep = PHYSFS_getDirSeparator(); + char *ptr; + + if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0) + return(1); + + ptr = (char *) allocator.Realloc(*dir, strlen(*dir) + strlen(dirsep) + 1); + if (!ptr) + { + allocator.Free(*dir); + return(0); + } /* if */ + + strcat(ptr, dirsep); + *dir = ptr; + return(1); +} /* appendDirSep */ + + +static char *calculateBaseDir(const char *argv0) +{ + char *retval = NULL; + const char *dirsep = NULL; + char *ptr = NULL; + + /* Give the platform layer first shot at this. */ + retval = __PHYSFS_platformCalcBaseDir(argv0); + if (retval != NULL) + return(retval); + + /* We need argv0 to go on. */ + BAIL_IF_MACRO(argv0 == NULL, ERR_ARGV0_IS_NULL, NULL); + + dirsep = PHYSFS_getDirSeparator(); + if (strlen(dirsep) == 1) /* fast path. */ + ptr = strrchr(argv0, *dirsep); + else + { + ptr = strstr(argv0, dirsep); + if (ptr != NULL) + { + char *p = ptr; + while (p != NULL) + { + ptr = p; + p = strstr(p + 1, dirsep); + } /* while */ + } /* if */ + } /* else */ + + if (ptr != NULL) + { + size_t size = (size_t) (ptr - argv0); + retval = (char *) allocator.Malloc(size + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + memcpy(retval, argv0, size); + retval[size] = '\0'; + return(retval); + } /* if */ + + /* argv0 wasn't helpful. */ + BAIL_MACRO(ERR_INVALID_ARGUMENT, NULL); + return(NULL); +} /* calculateBaseDir */ + + +static int initializeMutexes(void) +{ + errorLock = __PHYSFS_platformCreateMutex(); + if (errorLock == NULL) + goto initializeMutexes_failed; + + stateLock = __PHYSFS_platformCreateMutex(); + if (stateLock == NULL) + goto initializeMutexes_failed; + + return(1); /* success. */ + +initializeMutexes_failed: + if (errorLock != NULL) + __PHYSFS_platformDestroyMutex(errorLock); + + if (stateLock != NULL) + __PHYSFS_platformDestroyMutex(stateLock); + + errorLock = stateLock = NULL; + return(0); /* failed. */ +} /* initializeMutexes */ + + +static void setDefaultAllocator(void); + +int PHYSFS_init(const char *argv0) +{ + char *ptr; + + BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); + + if (!externalAllocator) + setDefaultAllocator(); + + if (allocator.Init != NULL) + BAIL_IF_MACRO(!allocator.Init(), NULL, 0); + + BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0); + + BAIL_IF_MACRO(!initializeMutexes(), NULL, 0); + + baseDir = calculateBaseDir(argv0); + BAIL_IF_MACRO(baseDir == NULL, NULL, 0); + + /* !!! FIXME: only call this if we got this from argv0 (unreliable). */ + ptr = __PHYSFS_platformRealPath(baseDir); + allocator.Free(baseDir); + BAIL_IF_MACRO(ptr == NULL, NULL, 0); + baseDir = ptr; + + BAIL_IF_MACRO(!appendDirSep(&baseDir), NULL, 0); + + userDir = calculateUserDir(); + if ((userDir == NULL) || (!appendDirSep(&userDir))) + { + allocator.Free(baseDir); + baseDir = NULL; + return(0); + } /* if */ + + initialized = 1; + + /* This makes sure that the error subsystem is initialized. */ + __PHYSFS_setError(PHYSFS_getLastError()); + + return(1); +} /* PHYSFS_init */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int closeFileHandleList(FileHandle **list) +{ + FileHandle *i; + FileHandle *next = NULL; + + for (i = *list; i != NULL; i = next) + { + next = i->next; + if (!i->funcs->fileClose(i->opaque)) + { + *list = i; + return(0); + } /* if */ + + allocator.Free(i); + } /* for */ + + *list = NULL; + return(1); +} /* closeFileHandleList */ + + +/* MAKE SURE you hold the stateLock before calling this! */ +static void freeSearchPath(void) +{ + DirHandle *i; + DirHandle *next = NULL; + + closeFileHandleList(&openReadList); + + if (searchPath != NULL) + { + for (i = searchPath; i != NULL; i = next) + { + next = i->next; + freeDirHandle(i, openReadList); + } /* for */ + searchPath = NULL; + } /* if */ +} /* freeSearchPath */ + + +int PHYSFS_deinit(void) +{ + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), NULL, 0); + + closeFileHandleList(&openWriteList); + BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0); + + freeSearchPath(); + freeErrorMessages(); + + if (baseDir != NULL) + { + allocator.Free(baseDir); + baseDir = NULL; + } /* if */ + + if (userDir != NULL) + { + allocator.Free(userDir); + userDir = NULL; + } /* if */ + + allowSymLinks = 0; + initialized = 0; + + __PHYSFS_platformDestroyMutex(errorLock); + __PHYSFS_platformDestroyMutex(stateLock); + + if (allocator.Deinit != NULL) + allocator.Deinit(); + + errorLock = stateLock = NULL; + return(1); +} /* PHYSFS_deinit */ + + +int PHYSFS_isInit(void) +{ + return(initialized); +} /* PHYSFS_isInit */ + + +const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) +{ + return(supported_types); +} /* PHYSFS_supportedArchiveTypes */ + + +void PHYSFS_freeList(void *list) +{ + void **i; + for (i = (void **) list; *i != NULL; i++) + allocator.Free(*i); + + allocator.Free(list); +} /* PHYSFS_freeList */ + + +const char *PHYSFS_getDirSeparator(void) +{ + return(__PHYSFS_platformDirSeparator); +} /* PHYSFS_getDirSeparator */ + + +char **PHYSFS_getCdRomDirs(void) +{ + return(doEnumStringList(__PHYSFS_platformDetectAvailableCDs)); +} /* PHYSFS_getCdRomDirs */ + + +void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data) +{ + __PHYSFS_platformDetectAvailableCDs(callback, data); +} /* PHYSFS_getCdRomDirsCallback */ + + +const char *PHYSFS_getBaseDir(void) +{ + return(baseDir); /* this is calculated in PHYSFS_init()... */ +} /* PHYSFS_getBaseDir */ + + +const char *PHYSFS_getUserDir(void) +{ + return(userDir); /* this is calculated in PHYSFS_init()... */ +} /* PHYSFS_getUserDir */ + + +const char *PHYSFS_getWriteDir(void) +{ + const char *retval = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + if (writeDir != NULL) + retval = writeDir->dirName; + __PHYSFS_platformReleaseMutex(stateLock); + + return(retval); +} /* PHYSFS_getWriteDir */ + + +int PHYSFS_setWriteDir(const char *newDir) +{ + int retval = 1; + + __PHYSFS_platformGrabMutex(stateLock); + + if (writeDir != NULL) + { + BAIL_IF_MACRO_MUTEX(!freeDirHandle(writeDir, openWriteList), NULL, + stateLock, 0); + writeDir = NULL; + } /* if */ + + if (newDir != NULL) + { + writeDir = createDirHandle(newDir, NULL, 1); + retval = (writeDir != NULL); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + + return(retval); +} /* PHYSFS_setWriteDir */ + + +int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) +{ + DirHandle *dh; + DirHandle *prev = NULL; + DirHandle *i; + + BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0); + + if (mountPoint == NULL) + mountPoint = "/"; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + { + /* already in search path? */ + BAIL_IF_MACRO_MUTEX(strcmp(newDir, i->dirName)==0, NULL, stateLock, 1); + prev = i; + } /* for */ + + dh = createDirHandle(newDir, mountPoint, 0); + BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0); + + if (appendToPath) + { + if (prev == NULL) + searchPath = dh; + else + prev->next = dh; + } /* if */ + else + { + dh->next = searchPath; + searchPath = dh; + } /* else */ + + __PHYSFS_platformReleaseMutex(stateLock); + return(1); +} /* PHYSFS_mount */ + + +int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) +{ + return(PHYSFS_mount(newDir, NULL, appendToPath)); +} /* PHYSFS_addToSearchPath */ + + +int PHYSFS_removeFromSearchPath(const char *oldDir) +{ + DirHandle *i; + DirHandle *prev = NULL; + DirHandle *next = NULL; + + BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0); + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, oldDir) == 0) + { + next = i->next; + BAIL_IF_MACRO_MUTEX(!freeDirHandle(i, openReadList), NULL, + stateLock, 0); + + if (prev == NULL) + searchPath = next; + else + prev->next = next; + + BAIL_MACRO_MUTEX(NULL, stateLock, 1); + } /* if */ + prev = i; + } /* for */ + + BAIL_MACRO_MUTEX(ERR_NOT_IN_SEARCH_PATH, stateLock, 0); +} /* PHYSFS_removeFromSearchPath */ + + +char **PHYSFS_getSearchPath(void) +{ + return(doEnumStringList(PHYSFS_getSearchPathCallback)); +} /* PHYSFS_getSearchPath */ + + +const char *PHYSFS_getMountPoint(const char *dir) +{ + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, dir) == 0) + { + const char *retval = ((i->mountPoint) ? i->mountPoint : "/"); + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + + BAIL_MACRO(ERR_NOT_IN_SEARCH_PATH, NULL); +} /* PHYSFS_getMountPoint */ + + +void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data) +{ + DirHandle *i; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + callback(data, i->dirName); + + __PHYSFS_platformReleaseMutex(stateLock); +} /* PHYSFS_getSearchPathCallback */ + + +/* Split out to avoid stack allocation in a loop. */ +static void setSaneCfgAddPath(const char *i, const size_t l, const char *dirsep, + int archivesFirst) +{ + const char *d = PHYSFS_getRealDir(i); + const size_t allocsize = strlen(d) + strlen(dirsep) + l + 1; + char *str = (char *) __PHYSFS_smallAlloc(allocsize); + if (str != NULL) + { + sprintf(str, "%s%s%s", d, dirsep, i); + PHYSFS_addToSearchPath(str, archivesFirst == 0); + __PHYSFS_smallFree(str); + } /* if */ +} /* setSaneCfgAddPath */ + + +int PHYSFS_setSaneConfig(const char *organization, const char *appName, + const char *archiveExt, int includeCdRoms, + int archivesFirst) +{ + const char *basedir = PHYSFS_getBaseDir(); + const char *userdir = PHYSFS_getUserDir(); + const char *dirsep = PHYSFS_getDirSeparator(); + PHYSFS_uint64 len = 0; + char *str = NULL; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + + /* set write dir... */ + len = (strlen(userdir) + (strlen(organization) * 2) + + (strlen(appName) * 2) + (strlen(dirsep) * 3) + 2); + + str = (char *) __PHYSFS_smallAlloc(len); + + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0); + sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName); + + if (!PHYSFS_setWriteDir(str)) + { + int no_write = 0; + sprintf(str, ".%s/%s", organization, appName); + if ( (PHYSFS_setWriteDir(userdir)) && + (PHYSFS_mkdir(str)) ) + { + sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName); + if (!PHYSFS_setWriteDir(str)) + no_write = 1; + } /* if */ + else + { + no_write = 1; + } /* else */ + + if (no_write) + { + PHYSFS_setWriteDir(NULL); /* just in case. */ + __PHYSFS_smallFree(str); + BAIL_MACRO(ERR_CANT_SET_WRITE_DIR, 0); + } /* if */ + } /* if */ + + /* Put write dir first in search path... */ + PHYSFS_addToSearchPath(str, 0); + __PHYSFS_smallFree(str); + + /* Put base path on search path... */ + PHYSFS_addToSearchPath(basedir, 1); + + /* handle CD-ROMs... */ + if (includeCdRoms) + { + char **cds = PHYSFS_getCdRomDirs(); + char **i; + for (i = cds; *i != NULL; i++) + PHYSFS_addToSearchPath(*i, 1); + + PHYSFS_freeList(cds); + } /* if */ + + /* Root out archives, and add them to search path... */ + if (archiveExt != NULL) + { + char **rc = PHYSFS_enumerateFiles("/"); + char **i; + size_t extlen = strlen(archiveExt); + char *ext; + + for (i = rc; *i != NULL; i++) + { + size_t l = strlen(*i); + if ((l > extlen) && ((*i)[l - extlen - 1] == '.')) + { + ext = (*i) + (l - extlen); + if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0) + setSaneCfgAddPath(*i, l, dirsep, archivesFirst); + } /* if */ + } /* for */ + + PHYSFS_freeList(rc); + } /* if */ + + return(1); +} /* PHYSFS_setSaneConfig */ + + +void PHYSFS_permitSymbolicLinks(int allow) +{ + allowSymLinks = allow; +} /* PHYSFS_permitSymbolicLinks */ + + +int PHYSFS_symbolicLinksPermitted(void) +{ + return(allowSymLinks); +} /* PHYSFS_symbolicLinksPermitted */ + + +/* string manipulation in C makes my ass itch. */ +char *__PHYSFS_convertToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + const char *dirsep = __PHYSFS_platformDirSeparator; + size_t sepsize = strlen(dirsep); + char *str; + char *i1; + char *i2; + size_t allocSize; + + while (*dirName == '/') /* !!! FIXME: pass through sanitize function. */ + dirName++; + + allocSize = strlen(dirName) + 1; + if (prepend != NULL) + allocSize += strlen(prepend) + sepsize; + if (append != NULL) + allocSize += strlen(append) + sepsize; + + /* make sure there's enough space if the dir separator is bigger. */ + if (sepsize > 1) + { + str = (char *) dirName; + do + { + str = strchr(str, '/'); + if (str != NULL) + { + allocSize += (sepsize - 1); + str++; + } /* if */ + } while (str != NULL); + } /* if */ + + str = (char *) allocator.Malloc(allocSize); + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend == NULL) + *str = '\0'; + else + { + strcpy(str, prepend); + strcat(str, dirsep); + } /* else */ + + for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++) + { + if (*i1 == '/') + { + strcpy(i2, dirsep); + i2 += sepsize; + } /* if */ + else + { + *i2 = *i1; + } /* else */ + } /* for */ + *i2 = '\0'; + + if (append) + { + strcat(str, dirsep); + strcat(str, append); + } /* if */ + + return(str); +} /* __PHYSFS_convertToDependent */ + + +/* + * Verify that (fname) (in platform-independent notation), in relation + * to (h) is secure. That means that each element of fname is checked + * for symlinks (if they aren't permitted). This also allows for quick + * rejection of files that exist outside an archive's mountpoint. + * + * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs + * at a time), you should always pass zero for "allowMissing" for efficiency. + * + * (fname) must point to an output from sanitizePlatformIndependentPath(), + * since it will make sure that path names are in the right format for + * passing certain checks. It will also do checks for "insecure" pathnames + * like ".." which should be done once instead of once per archive. This also + * gives us license to treat (fname) as scratch space in this function. + * + * Returns non-zero if string is safe, zero if there's a security issue. + * PHYSFS_getLastError() will specify what was wrong. (*fname) will be + * updated to point past any mount point elements so it is prepared to + * be used with the archiver directly. + */ +static int verifyPath(DirHandle *h, char **_fname, int allowMissing) +{ + char *fname = *_fname; + int retval = 1; + char *start; + char *end; + + if (*fname == '\0') /* quick rejection. */ + return(1); + + /* !!! FIXME: This codeblock sucks. */ + if (h->mountPoint != NULL) /* NULL mountpoint means "/". */ + { + size_t mntpntlen = strlen(h->mountPoint); + size_t len = strlen(fname); + assert(mntpntlen > 1); /* root mount points should be NULL. */ + /* not under the mountpoint, so skip this archive. */ + BAIL_IF_MACRO(len < mntpntlen-1, ERR_NO_SUCH_PATH, 0); + /* !!! FIXME: Case insensitive? */ + retval = strncmp(h->mountPoint, fname, mntpntlen-1); + BAIL_IF_MACRO(retval != 0, ERR_NO_SUCH_PATH, 0); + if (len > mntpntlen-1) /* corner case... */ + BAIL_IF_MACRO(fname[mntpntlen-1] != '/', ERR_NO_SUCH_PATH, 0); + fname += mntpntlen-1; /* move to start of actual archive path. */ + if (*fname == '/') + fname++; + *_fname = fname; /* skip mountpoint for later use. */ + retval = 1; /* may be reset, below. */ + } /* if */ + + start = fname; + if (!allowSymLinks) + { + while (1) + { + int rc = 0; + end = strchr(start, '/'); + + if (end != NULL) *end = '\0'; + rc = h->funcs->isSymLink(h->opaque, fname, &retval); + if (end != NULL) *end = '/'; + + BAIL_IF_MACRO(rc, ERR_SYMLINK_DISALLOWED, 0); /* insecure. */ + + /* break out early if path element is missing. */ + if (!retval) + { + /* + * We need to clear it if it's the last element of the path, + * since this might be a non-existant file we're opening + * for writing... + */ + if ((end == NULL) || (allowMissing)) + retval = 1; + break; + } /* if */ + + if (end == NULL) + break; + + start = end + 1; + } /* while */ + } /* if */ + + return(retval); +} /* verifyPath */ + + +static int doMkdir(const char *_dname, char *dname) +{ + DirHandle *h; + char *start; + char *end; + int retval = 0; + int exists = 1; /* force existance check on first path element. */ + + BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_dname, dname), NULL, 0); + + __PHYSFS_platformGrabMutex(stateLock); + BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MACRO_MUTEX(!verifyPath(h, &dname, 1), NULL, stateLock, 0); + + start = dname; + while (1) + { + end = strchr(start, '/'); + if (end != NULL) + *end = '\0'; + + /* only check for existance if all parent dirs existed, too... */ + if (exists) + retval = h->funcs->isDirectory(h->opaque, dname, &exists); + + if (!exists) + retval = h->funcs->mkdir(h->opaque, dname); + + if (!retval) + break; + + if (end == NULL) + break; + + *end = '/'; + start = end + 1; + } /* while */ + + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); +} /* doMkdir */ + + +int PHYSFS_mkdir(const char *_dname) +{ + int retval = 0; + char *dname; + size_t len; + + BAIL_IF_MACRO(_dname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_dname) + 1; + dname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(dname == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doMkdir(_dname, dname); + __PHYSFS_smallFree(dname); + return(retval); +} /* PHYSFS_mkdir */ + + +static int doDelete(const char *_fname, char *fname) +{ + int retval; + DirHandle *h; + BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_fname, fname), NULL, 0); + + __PHYSFS_platformGrabMutex(stateLock); + + BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MACRO_MUTEX(!verifyPath(h, &fname, 0), NULL, stateLock, 0); + retval = h->funcs->remove(h->opaque, fname); + + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); +} /* doDelete */ + + +int PHYSFS_delete(const char *_fname) +{ + int retval; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doDelete(_fname, fname); + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_delete */ + + +const char *PHYSFS_getRealDir(const char *_fname) +{ + const char *retval = NULL; + char *fname = NULL; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, NULL); + len = strlen(_fname) + 1; + fname = __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, NULL); + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (retval == NULL)); i = i->next) + { + char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) + retval = i->dirName; + else if (verifyPath(i, &arcfname, 0)) + { + if (i->funcs->exists(i->opaque, arcfname)) + retval = i->dirName; + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_getRealDir */ + + +static int locateInStringList(const char *str, + char **list, + PHYSFS_uint32 *pos) +{ + PHYSFS_uint32 len = *pos; + PHYSFS_uint32 half_len; + PHYSFS_uint32 lo = 0; + PHYSFS_uint32 middle; + int cmp; + + while (len > 0) + { + half_len = len >> 1; + middle = lo + half_len; + cmp = strcmp(list[middle], str); + + if (cmp == 0) /* it's in the list already. */ + return(1); + else if (cmp > 0) + len = half_len; + else + { + lo = middle + 1; + len -= half_len + 1; + } /* else */ + } /* while */ + + *pos = lo; + return(0); +} /* locateInStringList */ + + +static void enumFilesCallback(void *data, const char *origdir, const char *str) +{ + PHYSFS_uint32 pos; + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + /* + * See if file is in the list already, and if not, insert it in there + * alphabetically... + */ + pos = pecd->size; + if (locateInStringList(str, pecd->list, &pos)) + return; /* already in the list. */ + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + return; /* better luck next time. */ + + strcpy(newstr, str); + + if (pos != pecd->size) + { + memmove(&pecd->list[pos+1], &pecd->list[pos], + sizeof (char *) * ((pecd->size) - pos)); + } /* if */ + + pecd->list[pos] = newstr; + pecd->size++; +} /* enumFilesCallback */ + + +char **PHYSFS_enumerateFiles(const char *path) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL); + PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd); + ecd.list[ecd.size] = NULL; + return(ecd.list); +} /* PHYSFS_enumerateFiles */ + + +/* + * Broke out to seperate function so we can use stack allocation gratuitously. + */ +static void enumerateFromMountPoint(DirHandle *i, const char *arcfname, + PHYSFS_EnumFilesCallback callback, + const char *_fname, void *data) +{ + const size_t len = strlen(arcfname); + char *ptr = NULL; + char *end = NULL; + const size_t slen = strlen(i->mountPoint) + 1; + char *mountPoint = (char *) __PHYSFS_smallAlloc(slen); + + if (mountPoint == NULL) + return; /* oh well. */ + + strcpy(mountPoint, i->mountPoint); + ptr = mountPoint + ((len) ? len + 1 : 0); + end = strchr(ptr, '/'); + assert(end); /* should always find a terminating '/'. */ + *end = '\0'; + callback(data, _fname, ptr); + __PHYSFS_smallFree(mountPoint); +} /* enumerateFromMountPoint */ + + +/* !!! FIXME: this should report error conditions. */ +void PHYSFS_enumerateFilesCallback(const char *_fname, + PHYSFS_EnumFilesCallback callback, + void *data) +{ + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, ) /*0*/; + BAIL_IF_MACRO(callback == NULL, ERR_INVALID_ARGUMENT, ) /*0*/; + + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, ) /*0*/; + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i; + int noSyms; + + __PHYSFS_platformGrabMutex(stateLock); + noSyms = !allowSymLinks; + for (i = searchPath; i != NULL; i = i->next) + { + char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) + enumerateFromMountPoint(i, arcfname, callback, _fname, data); + + else if (verifyPath(i, &arcfname, 0)) + { + i->funcs->enumerateFiles(i->opaque, arcfname, noSyms, + callback, _fname, data); + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); +} /* PHYSFS_enumerateFilesCallback */ + + +int PHYSFS_exists(const char *fname) +{ + return(PHYSFS_getRealDir(fname) != NULL); +} /* PHYSFS_exists */ + + +PHYSFS_sint64 PHYSFS_getLastModTime(const char *_fname) +{ + PHYSFS_sint64 retval = -1; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, -1); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, -1); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + if (*fname == '\0') /* eh...punt if it's the root dir. */ + retval = 1; /* !!! FIXME: Maybe this should be an error? */ + else + { + DirHandle *i; + int exists = 0; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + exists = partOfMountPoint(i, arcfname); + if (exists) + retval = 1; /* !!! FIXME: What's the right value? */ + else if (verifyPath(i, &arcfname, 0)) + { + retval = i->funcs->getLastModTime(i->opaque, arcfname, + &exists); + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + } /* if */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_getLastModTime */ + + +int PHYSFS_isDirectory(const char *_fname) +{ + int retval = 0; + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (!sanitizePlatformIndependentPath(_fname, fname)) + retval = 0; + + else if (*fname == '\0') + retval = 1; /* Root is always a dir. :) */ + + else + { + DirHandle *i; + int exists = 0; + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + if ((exists = partOfMountPoint(i, arcfname)) != 0) + retval = 1; + else if (verifyPath(i, &arcfname, 0)) + retval = i->funcs->isDirectory(i->opaque, arcfname, &exists); + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_isDirectory */ + + +int PHYSFS_isSymbolicLink(const char *_fname) +{ + int retval = 0; + size_t len; + char *fname; + + BAIL_IF_MACRO(!allowSymLinks, ERR_SYMLINK_DISALLOWED, 0); + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (!sanitizePlatformIndependentPath(_fname, fname)) + retval = 0; + + else if (*fname == '\0') + retval = 1; /* Root is never a symlink. */ + + else + { + DirHandle *i; + int fileExists = 0; + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!fileExists)); i = i->next) + { + char *arcfname = fname; + if ((fileExists = partOfMountPoint(i, arcfname)) != 0) + retval = 0; /* virtual dir...not a symlink. */ + else if (verifyPath(i, &arcfname, 0)) + retval = i->funcs->isSymLink(i->opaque, arcfname, &fileExists); + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_isSymbolicLink */ + + +static PHYSFS_File *doOpenWrite(const char *_fname, int appending) +{ + FileHandle *fh = NULL; + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + void *opaque = NULL; + DirHandle *h = NULL; + const PHYSFS_Archiver *f; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF_MACRO(!writeDir, ERR_NO_WRITE_DIR, doOpenWriteEnd); + + h = writeDir; + GOTO_IF_MACRO(!verifyPath(h, &fname, 0), NULL, doOpenWriteEnd); + + f = h->funcs; + if (appending) + opaque = f->openAppend(h->opaque, fname); + else + opaque = f->openWrite(h->opaque, fname); + + GOTO_IF_MACRO(opaque == NULL, NULL, doOpenWriteEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + f->fileClose(opaque); + GOTO_MACRO(ERR_OUT_OF_MEMORY, doOpenWriteEnd); + } /* if */ + else + { + memset(fh, '\0', sizeof (FileHandle)); + fh->opaque = opaque; + fh->dirHandle = h; + fh->funcs = h->funcs; + fh->next = openWriteList; + openWriteList = fh; + } /* else */ + + doOpenWriteEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return((PHYSFS_File *) fh); +} /* doOpenWrite */ + + +PHYSFS_File *PHYSFS_openWrite(const char *filename) +{ + return(doOpenWrite(filename, 0)); +} /* PHYSFS_openWrite */ + + +PHYSFS_File *PHYSFS_openAppend(const char *filename) +{ + return(doOpenWrite(filename, 1)); +} /* PHYSFS_openAppend */ + + +PHYSFS_File *PHYSFS_openRead(const char *_fname) +{ + FileHandle *fh = NULL; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + int fileExists = 0; + DirHandle *i = NULL; + fvoid *opaque = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF_MACRO(!searchPath, ERR_NO_SUCH_PATH, openReadEnd); + + /* !!! FIXME: Why aren't we using a for loop here? */ + i = searchPath; + + do + { + char *arcfname = fname; + if (verifyPath(i, &arcfname, 0)) + { + opaque = i->funcs->openRead(i->opaque, arcfname, &fileExists); + if (opaque) + break; + } /* if */ + i = i->next; + } while ((i != NULL) && (!fileExists)); + + /* !!! FIXME: may not set an error if openRead didn't fail. */ + GOTO_IF_MACRO(opaque == NULL, NULL, openReadEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + i->funcs->fileClose(opaque); + GOTO_MACRO(ERR_OUT_OF_MEMORY, openReadEnd); + } /* if */ + + memset(fh, '\0', sizeof (FileHandle)); + fh->opaque = opaque; + fh->forReading = 1; + fh->dirHandle = i; + fh->funcs = i->funcs; + fh->next = openReadList; + openReadList = fh; + + openReadEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return((PHYSFS_File *) fh); +} /* PHYSFS_openRead */ + + +static int closeHandleInOpenList(FileHandle **list, FileHandle *handle) +{ + FileHandle *prev = NULL; + FileHandle *i; + int rc = 1; + + for (i = *list; i != NULL; i = i->next) + { + if (i == handle) /* handle is in this list? */ + { + PHYSFS_uint8 *tmp = handle->buffer; + rc = PHYSFS_flush((PHYSFS_File *) handle); + if (rc) + rc = handle->funcs->fileClose(handle->opaque); + if (!rc) + return(-1); + + if (tmp != NULL) /* free any associated buffer. */ + allocator.Free(tmp); + + if (prev == NULL) + *list = handle->next; + else + prev->next = handle->next; + + allocator.Free(handle); + return(1); + } /* if */ + prev = i; + } /* for */ + + return(0); +} /* closeHandleInOpenList */ + + +int PHYSFS_close(PHYSFS_File *_handle) +{ + FileHandle *handle = (FileHandle *) _handle; + int rc; + + __PHYSFS_platformGrabMutex(stateLock); + + /* -1 == close failure. 0 == not found. 1 == success. */ + rc = closeHandleInOpenList(&openReadList, handle); + BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0); + if (!rc) + { + rc = closeHandleInOpenList(&openWriteList, handle); + BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + BAIL_IF_MACRO(!rc, ERR_NOT_A_HANDLE, 0); + return(1); +} /* PHYSFS_close */ + + +static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval = 0; + PHYSFS_uint32 remainder = 0; + + while (objCount > 0) + { + PHYSFS_uint32 buffered = fh->buffill - fh->bufpos; + PHYSFS_uint64 mustread = (objSize * objCount) - remainder; + PHYSFS_uint32 copied; + + if (buffered == 0) /* need to refill buffer? */ + { + PHYSFS_sint64 rc = fh->funcs->read(fh->opaque, fh->buffer, + 1, fh->bufsize); + if (rc <= 0) + { + fh->bufpos -= remainder; + return(((rc == -1) && (retval == 0)) ? -1 : retval); + } /* if */ + + buffered = fh->buffill = (PHYSFS_uint32) rc; + fh->bufpos = 0; + } /* if */ + + if (buffered > mustread) + buffered = (PHYSFS_uint32) mustread; + + memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered); + buffer = ((PHYSFS_uint8 *) buffer) + buffered; + fh->bufpos += buffered; + buffered += remainder; /* take remainder into account. */ + copied = (buffered / objSize); + remainder = (buffered % objSize); + retval += copied; + objCount -= copied; + } /* while */ + + return(retval); +} /* doBufferedRead */ + + +PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + BAIL_IF_MACRO(!fh->forReading, ERR_FILE_ALREADY_OPEN_W, -1); + BAIL_IF_MACRO(objSize == 0, NULL, 0); + BAIL_IF_MACRO(objCount == 0, NULL, 0); + if (fh->buffer != NULL) + return(doBufferedRead(fh, buffer, objSize, objCount)); + + return(fh->funcs->read(fh->opaque, buffer, objSize, objCount)); +} /* PHYSFS_read */ + + +static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + /* whole thing fits in the buffer? */ + if (fh->buffill + (objSize * objCount) < fh->bufsize) + { + memcpy(fh->buffer + fh->buffill, buffer, objSize * objCount); + fh->buffill += (objSize * objCount); + return(objCount); + } /* if */ + + /* would overflow buffer. Flush and then write the new objects, too. */ + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1); + return(fh->funcs->write(fh->opaque, buffer, objSize, objCount)); +} /* doBufferedWrite */ + + +PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + BAIL_IF_MACRO(fh->forReading, ERR_FILE_ALREADY_OPEN_R, -1); + BAIL_IF_MACRO(objSize == 0, NULL, 0); + BAIL_IF_MACRO(objCount == 0, NULL, 0); + if (fh->buffer != NULL) + return(doBufferedWrite(handle, buffer, objSize, objCount)); + + return(fh->funcs->write(fh->opaque, buffer, objSize, objCount)); +} /* PHYSFS_write */ + + +int PHYSFS_eof(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + + if (!fh->forReading) /* never EOF on files opened for write/append. */ + return(0); + + /* eof if buffer is empty and archiver says so. */ + return((fh->bufpos == fh->buffill) && (fh->funcs->eof(fh->opaque))); +} /* PHYSFS_eof */ + + +PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_sint64 pos = fh->funcs->tell(fh->opaque); + PHYSFS_sint64 retval = fh->forReading ? + (pos - fh->buffill) + fh->bufpos : + (pos + fh->buffill); + return(retval); +} /* PHYSFS_tell */ + + +int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) +{ + FileHandle *fh = (FileHandle *) handle; + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); + + if (fh->buffer && fh->forReading) + { + /* avoid throwing away our precious buffer if seeking within it. */ + PHYSFS_sint64 offset = pos - PHYSFS_tell(handle); + if ( /* seeking within the already-buffered range? */ + ((offset >= 0) && (offset <= fh->buffill - fh->bufpos)) /* fwd */ + || ((offset < 0) && (-offset <= fh->bufpos)) /* backward */ ) + { + fh->bufpos += (PHYSFS_uint32) offset; + return(1); /* successful seek */ + } /* if */ + } /* if */ + + /* we have to fall back to a 'raw' seek. */ + fh->buffill = fh->bufpos = 0; + return(fh->funcs->seek(fh->opaque, pos)); +} /* PHYSFS_seek */ + + +PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + return(fh->funcs->fileLength(fh->opaque)); +} /* PHYSFS_filelength */ + + +int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_uint32 bufsize; + + /* !!! FIXME: Unlocalized string. */ + BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, "buffer must fit in 32-bits", 0); + bufsize = (PHYSFS_uint32) _bufsize; + + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); + + /* + * For reads, we need to move the file pointer to where it would be + * if we weren't buffering, so that the next read will get the + * right chunk of stuff from the file. PHYSFS_flush() handles writes. + */ + if ((fh->forReading) && (fh->buffill != fh->bufpos)) + { + PHYSFS_uint64 pos; + PHYSFS_sint64 curpos = fh->funcs->tell(fh->opaque); + BAIL_IF_MACRO(curpos == -1, NULL, 0); + pos = ((curpos - fh->buffill) + fh->bufpos); + BAIL_IF_MACRO(!fh->funcs->seek(fh->opaque, pos), NULL, 0); + } /* if */ + + if (bufsize == 0) /* delete existing buffer. */ + { + if (fh->buffer != NULL) + { + allocator.Free(fh->buffer); + fh->buffer = NULL; + } /* if */ + } /* if */ + + else + { + PHYSFS_uint8 *newbuf; + newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize); + BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0); + fh->buffer = newbuf; + } /* else */ + + fh->bufsize = bufsize; + fh->buffill = fh->bufpos = 0; + return(1); +} /* PHYSFS_setBuffer */ + + +int PHYSFS_flush(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_sint64 rc; + + if ((fh->forReading) || (fh->bufpos == fh->buffill)) + return(1); /* open for read or buffer empty are successful no-ops. */ + + /* dump buffer to disk. */ + rc = fh->funcs->write(fh->opaque, fh->buffer + fh->bufpos, + fh->buffill - fh->bufpos, 1); + BAIL_IF_MACRO(rc <= 0, NULL, 0); + fh->bufpos = fh->buffill = 0; + return(1); +} /* PHYSFS_flush */ + + +int PHYSFS_setAllocator(const PHYSFS_Allocator *a) +{ + BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); + externalAllocator = (a != NULL); + if (externalAllocator) + memcpy(&allocator, a, sizeof (PHYSFS_Allocator)); + + return(1); +} /* PHYSFS_setAllocator */ + + +static void *mallocAllocatorMalloc(PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + #undef malloc + return(malloc((size_t) s)); +} /* mallocAllocatorMalloc */ + + +static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + #undef realloc + return(realloc(ptr, (size_t) s)); +} /* mallocAllocatorRealloc */ + + +static void mallocAllocatorFree(void *ptr) +{ + #undef free + free(ptr); +} /* mallocAllocatorFree */ + + +static void setDefaultAllocator(void) +{ + assert(!externalAllocator); + if (!__PHYSFS_platformSetDefaultAllocator(&allocator)) + { + allocator.Init = NULL; + allocator.Deinit = NULL; + allocator.Malloc = mallocAllocatorMalloc; + allocator.Realloc = mallocAllocatorRealloc; + allocator.Free = mallocAllocatorFree; + } /* if */ +} /* setDefaultAllocator */ + + +void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len) +{ + const char useHeap = ((ptr == NULL) ? 1 : 0); + if (useHeap) /* too large for stack allocation or alloca() failed. */ + ptr = allocator.Malloc(len+1); + + if (ptr != NULL) + { + char *retval = (char *) ptr; + /*printf("%s alloc'd (%d) bytes at (%p).\n", + useHeap ? "heap" : "stack", (int) len, ptr);*/ + *retval = useHeap; + return(retval+1); + } /* if */ + + return(NULL); /* allocation failed. */ +} /* __PHYSFS_initSmallAlloc */ + + +void __PHYSFS_smallFree(void *ptr) +{ + if (ptr != NULL) + { + char *block = ((char *) ptr) - 1; + const char useHeap = *block; + if (useHeap) + allocator.Free(block); + /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/ + } /* if */ +} /* __PHYSFS_smallFree */ + +/* end of physfs.c ... */ + diff --git a/3rdparty/physfs/physfs.h b/3rdparty/physfs/physfs.h new file mode 100644 index 0000000..e02f3fc --- /dev/null +++ b/3rdparty/physfs/physfs.h @@ -0,0 +1,2396 @@ +/** + * \file physfs.h + * + * Main header file for PhysicsFS. + */ + +/** + * \mainpage PhysicsFS + * + * The latest version of PhysicsFS can be found at: + * http://icculus.org/physfs/ + * + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * This API gives you access to a system file system in ways superior to the + * stdio or system i/o calls. The brief benefits: + * + * - It's portable. + * - It's safe. No file access is permitted outside the specified dirs. + * - It's flexible. Archives (.ZIP files) can be used transparently as + * directory structures. + * + * This system is largely inspired by Quake 3's PK3 files and the related + * fs_* cvars. If you've ever tinkered with these, then this API will be + * familiar to you. + * + * With PhysicsFS, you have a single writing directory and multiple + * directories (the "search path") for reading. You can think of this as a + * filesystem within a filesystem. If (on Windows) you were to set the + * writing directory to "C:\MyGame\MyWritingDirectory", then no PHYSFS calls + * could touch anything above this directory, including the "C:\MyGame" and + * "C:\" directories. This prevents an application's internal scripting + * language from piddling over c:\\config.sys, for example. If you'd rather + * give PHYSFS full access to the system's REAL file system, set the writing + * dir to "C:\", but that's generally A Bad Thing for several reasons. + * + * Drive letters are hidden in PhysicsFS once you set up your initial paths. + * The search path creates a single, hierarchical directory structure. + * Not only does this lend itself well to general abstraction with archives, + * it also gives better support to operating systems like MacOS and Unix. + * Generally speaking, you shouldn't ever hardcode a drive letter; not only + * does this hurt portability to non-Microsoft OSes, but it limits your win32 + * users to a single drive, too. Use the PhysicsFS abstraction functions and + * allow user-defined configuration options, too. When opening a file, you + * specify it like it was on a Unix filesystem: if you want to write to + * "C:\MyGame\MyConfigFiles\game.cfg", then you might set the write dir to + * "C:\MyGame" and then open "MyConfigFiles/game.cfg". This gives an + * abstraction across all platforms. Specifying a file in this way is termed + * "platform-independent notation" in this documentation. Specifying a + * a filename in a form such as "C:\mydir\myfile" or + * "MacOS hard drive:My Directory:My File" is termed "platform-dependent + * notation". The only time you use platform-dependent notation is when + * setting up your write directory and search path; after that, all file + * access into those directories are done with platform-independent notation. + * + * All files opened for writing are opened in relation to the write directory, + * which is the root of the writable filesystem. When opening a file for + * reading, PhysicsFS goes through the search path. This is NOT the + * same thing as the PATH environment variable. An application using + * PhysicsFS specifies directories to be searched which may be actual + * directories, or archive files that contain files and subdirectories of + * their own. See the end of these docs for currently supported archive + * formats. + * + * Once the search path is defined, you may open files for reading. If you've + * got the following search path defined (to use a win32 example again): + * + * - C:\\mygame + * - C:\\mygame\\myuserfiles + * - D:\\mygamescdromdatafiles + * - C:\\mygame\\installeddatafiles.zip + * + * Then a call to PHYSFS_openRead("textfiles/myfile.txt") (note the directory + * separator, lack of drive letter, and lack of dir separator at the start of + * the string; this is platform-independent notation) will check for + * C:\\mygame\\textfiles\\myfile.txt, then + * C:\\mygame\\myuserfiles\\textfiles\\myfile.txt, then + * D:\\mygamescdromdatafiles\\textfiles\\myfile.txt, then, finally, for + * textfiles\\myfile.txt inside of C:\\mygame\\installeddatafiles.zip. + * Remember that most archive types and platform filesystems store their + * filenames in a case-sensitive manner, so you should be careful to specify + * it correctly. + * + * Files opened through PhysicsFS may NOT contain "." or ".." or ":" as dir + * elements. Not only are these meaningless on MacOS Classic and/or Unix, + * they are a security hole. Also, symbolic links (which can be found in + * some archive types and directly in the filesystem on Unix platforms) are + * NOT followed until you call PHYSFS_permitSymbolicLinks(). That's left to + * your own discretion, as following a symlink can allow for access outside + * the write dir and search paths. For portability, there is no mechanism for + * creating new symlinks in PhysicsFS. + * + * The write dir is not included in the search path unless you specifically + * add it. While you CAN change the write dir as many times as you like, + * you should probably set it once and stick to it. Remember that your + * program will not have permission to write in every directory on Unix and + * NT systems. + * + * All files are opened in binary mode; there is no endline conversion for + * textfiles. Other than that, PhysicsFS has some convenience functions for + * platform-independence. There is a function to tell you the current + * platform's dir separator ("\\" on windows, "/" on Unix, ":" on MacOS), + * which is needed only to set up your search/write paths. There is a + * function to tell you what CD-ROM drives contain accessible discs, and a + * function to recommend a good search path, etc. + * + * A recommended order for the search path is the write dir, then the base dir, + * then the cdrom dir, then any archives discovered. Quake 3 does something + * like this, but moves the archives to the start of the search path. Build + * Engine games, like Duke Nukem 3D and Blood, place the archives last, and + * use the base dir for both searching and writing. There is a helper + * function (PHYSFS_setSaneConfig()) that puts together a basic configuration + * for you, based on a few parameters. Also see the comments on + * PHYSFS_getBaseDir(), and PHYSFS_getUserDir() for info on what those + * are and how they can help you determine an optimal search path. + * + * PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points + * in the search path. If a zipfile contains "maps/level.map" and you mount + * that archive at "mods/mymod", then you would have to open + * "mods/mymod/maps/level.map" to access the file, even though "mods/mymod" + * isn't actually specified in the .zip file. Unlike the Unix mentality of + * mounting a filesystem, "mods/mymod" doesn't actually have to exist when + * mounting the zipfile. It's a "virtual" directory. The mounting mechanism + * allows the developer to seperate archives in the tree and avoid trampling + * over files when added new archives, such as including mod support in a + * game...keeping external content on a tight leash in this manner can be of + * utmost importance to some applications. + * + * PhysicsFS is mostly thread safe. The error messages returned by + * PHYSFS_getLastError are unique by thread, and library-state-setting + * functions are mutex'd. For efficiency, individual file accesses are + * not locked, so you can not safely read/write/seek/close/etc the same + * file from two threads at the same time. Other race conditions are bugs + * that should be reported/patched. + * + * While you CAN use stdio/syscall file access in a program that has PHYSFS_* + * calls, doing so is not recommended, and you can not use system + * filehandles with PhysicsFS and vice versa. + * + * Note that archives need not be named as such: if you have a ZIP file and + * rename it with a .PKG extension, the file will still be recognized as a + * ZIP archive by PhysicsFS; the file's contents are used to determine its + * type where possible. + * + * Currently supported archive types: + * - .ZIP (pkZip/WinZip/Info-ZIP compatible) + * - .GRP (Build Engine groupfile archives) + * - .PAK (Quake I/II archive format) + * - .HOG (Descent I/II HOG file archives) + * - .MVL (Descent II movielib archives) + * - .WAD (DOOM engine archives) + * + * + * String policy for PhysicsFS 2.0 and later: + * + * PhysicsFS 1.0 could only deal with null-terminated ASCII strings. All high + * ASCII chars resulted in undefined behaviour, and there was no Unicode + * support at all. PhysicsFS 2.0 supports Unicode without breaking binary + * compatibility with the 1.0 API by using UTF-8 encoding of all strings + * passed in and out of the library. + * + * All strings passed through PhysicsFS are in null-terminated UTF-8 format. + * This means that if all you care about is English (ASCII characters <= 127) + * then you just use regular C strings. If you care about Unicode (and you + * should!) then you need to figure out what your platform wants, needs, and + * offers. If you are on Windows and build with Unicode support, your TCHAR + * strings are two bytes per character (this is called "UCS-2 encoding"). You + * should convert them to UTF-8 before handing them to PhysicsFS with + * PHYSFS_utf8FromUcs2(). If you're using Unix or Mac OS X, your wchar_t + * strings are four bytes per character ("UCS-4 encoding"). Use + * PHYSFS_utf8FromUcs4(). Mac OS X can give you UTF-8 directly from a + * CFString, and many Unixes generally give you C strings in UTF-8 format + * everywhere. If you have a single-byte high ASCII charset, like so-many + * European "codepages" you may be out of luck. We'll convert from "Latin1" + * to UTF-8 only, and never back to Latin1. If you're above ASCII 127, all + * bets are off: move to Unicode or use your platform's facilities. Passing a + * C string with high-ASCII data that isn't UTF-8 encoded will NOT do what + * you expect! + * + * Naturally, there's also PHYSFS_utf8ToUcs2() and PHYSFS_utf8ToUcs4() to get + * data back into a format you like. Behind the scenes, PhysicsFS will use + * Unicode where possible: the UTF-8 strings on Windows will be converted + * and used with the multibyte Windows APIs, for example. + * + * PhysicsFS offers basic encoding conversion support, but not a whole string + * library. Get your stuff into whatever format you can work with. + * + * Some platforms and archivers don't offer full Unicode support behind the + * scenes. For example, OS/2 only offers "codepages" and the filesystem + * itself doesn't support multibyte encodings. We make an earnest effort to + * convert to/from the current locale here, but all bets are off if + * you want to hand an arbitrary Japanese character through to these systems. + * Modern OSes (Mac OS X, Linux, Windows, PocketPC, etc) should all be fine. + * Many game-specific archivers are seriously unprepared for Unicode (the + * Descent HOG/MVL and Build Engine GRP archivers, for example, only offer a + * DOS 8.3 filename, for example). Nothing can be done for these, but they + * tend to be legacy formats for existing content that was all ASCII (and + * thus, valid UTF-8) anyhow. Other formats, like .ZIP, don't explicitly + * offer Unicode support, but unofficially expect filenames to be UTF-8 + * encoded, and thus Just Work. Most everything does the right thing without + * bothering you, but it's good to be aware of these nuances in case they + * don't. + * + * + * Other stuff: + * + * Please see the file LICENSE.txt in the source's root directory for licensing + * and redistribution rights. + * + * Please see the file CREDITS.txt in the source's root directory for a more or + * less complete list of who's responsible for this. + * + * \author Ryan C. Gordon. + */ + +#ifndef _INCLUDE_PHYSFS_H_ +#define _INCLUDE_PHYSFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#if (defined _MSC_VER) +#define __EXPORT__ __declspec(dllexport) +#elif (__GNUC__ >= 3) +#define __EXPORT__ __attribute__((visibility("default"))) +#else +#define __EXPORT__ +#endif +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + +/** + * \typedef PHYSFS_uint8 + * \brief An unsigned, 8-bit integer type. + */ +typedef unsigned char PHYSFS_uint8; + +/** + * \typedef PHYSFS_sint8 + * \brief A signed, 8-bit integer type. + */ +typedef signed char PHYSFS_sint8; + +/** + * \typedef PHYSFS_uint16 + * \brief An unsigned, 16-bit integer type. + */ +typedef unsigned short PHYSFS_uint16; + +/** + * \typedef PHYSFS_sint16 + * \brief A signed, 16-bit integer type. + */ +typedef signed short PHYSFS_sint16; + +/** + * \typedef PHYSFS_uint32 + * \brief An unsigned, 32-bit integer type. + */ +typedef unsigned int PHYSFS_uint32; + +/** + * \typedef PHYSFS_sint32 + * \brief A signed, 32-bit integer type. + */ +typedef signed int PHYSFS_sint32; + +/** + * \typedef PHYSFS_uint64 + * \brief An unsigned, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_uint32! + */ + +/** + * \typedef PHYSFS_sint64 + * \brief A signed, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_sint32! + */ + + +#if (defined PHYSFS_NO_64BIT_SUPPORT) /* oh well. */ +typedef PHYSFS_uint32 PHYSFS_uint64; +typedef PHYSFS_sint32 PHYSFS_sint64; +#elif (defined _MSC_VER) +typedef signed __int64 PHYSFS_sint64; +typedef unsigned __int64 PHYSFS_uint64; +#else +typedef unsigned long long PHYSFS_uint64; +typedef signed long long PHYSFS_sint64; +#endif + + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +/* Make sure the types really have the right sizes */ +#define PHYSFS_COMPILE_TIME_ASSERT(name, x) \ + typedef int PHYSFS_dummy_ ## name[(x) * 2 - 1] + +PHYSFS_COMPILE_TIME_ASSERT(uint8, sizeof(PHYSFS_uint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(sint8, sizeof(PHYSFS_sint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(uint16, sizeof(PHYSFS_uint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(sint16, sizeof(PHYSFS_sint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(uint32, sizeof(PHYSFS_uint32) == 4); +PHYSFS_COMPILE_TIME_ASSERT(sint32, sizeof(PHYSFS_sint32) == 4); + +#ifndef PHYSFS_NO_64BIT_SUPPORT +PHYSFS_COMPILE_TIME_ASSERT(uint64, sizeof(PHYSFS_uint64) == 8); +PHYSFS_COMPILE_TIME_ASSERT(sint64, sizeof(PHYSFS_sint64) == 8); +#endif + +#undef PHYSFS_COMPILE_TIME_ASSERT + +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/** + * \struct PHYSFS_File + * \brief A PhysicsFS file handle. + * + * You get a pointer to one of these when you open a file for reading, + * writing, or appending via PhysicsFS. + * + * As you can see from the lack of meaningful fields, you should treat this + * as opaque data. Don't try to manipulate the file handle, just pass the + * pointer you got, unmolested, to various PhysicsFS APIs. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_close + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_seek + * \sa PHYSFS_tell + * \sa PHYSFS_eof + * \sa PHYSFS_setBuffer + * \sa PHYSFS_flush + */ +typedef struct PHYSFS_File +{ + void *opaque; /**< That's all you get. Don't touch. */ +} PHYSFS_File; + + +/** + * \def PHYSFS_file + * \brief 1.0 API compatibility define. + * + * PHYSFS_file is identical to PHYSFS_File. This #define is here for backwards + * compatibility with the 1.0 API, which had an inconsistent capitalization + * convention in this case. New code should use PHYSFS_File, as this #define + * may go away someday. + * + * \sa PHYSFS_File + */ +#define PHYSFS_file PHYSFS_File + + +/** + * \struct PHYSFS_ArchiveInfo + * \brief Information on various PhysicsFS-supported archives. + * + * This structure gives you details on what sort of archives are supported + * by this implementation of PhysicsFS. Archives tend to be things like + * ZIP files and such. + * + * \warning Not all binaries are created equal! PhysicsFS can be built with + * or without support for various archives. You can check with + * PHYSFS_supportedArchiveTypes() to see if your archive type is + * supported. + * + * \sa PHYSFS_supportedArchiveTypes + */ +typedef struct PHYSFS_ArchiveInfo +{ + const char *extension; /**< Archive file extension: "ZIP", for example. */ + const char *description; /**< Human-readable archive description. */ + const char *author; /**< Person who did support for this archive. */ + const char *url; /**< URL related to this archive */ +} PHYSFS_ArchiveInfo; + + +/** + * \struct PHYSFS_Version + * \brief Information the version of PhysicsFS in use. + * + * Represents the library's version as three levels: major revision + * (increments with massive changes, additions, and enhancements), + * minor revision (increments with backwards-compatible changes to the + * major revision), and patchlevel (increments with fixes to the minor + * revision). + * + * \sa PHYSFS_VERSION + * \sa PHYSFS_getLinkedVersion + */ +typedef struct PHYSFS_Version +{ + PHYSFS_uint8 major; /**< major revision */ + PHYSFS_uint8 minor; /**< minor revision */ + PHYSFS_uint8 patch; /**< patchlevel */ +} PHYSFS_Version; + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#define PHYSFS_VER_MAJOR 2 +#define PHYSFS_VER_MINOR 0 +#define PHYSFS_VER_PATCH 3 +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/* PhysicsFS state stuff ... */ + +/** + * \def PHYSFS_VERSION(x) + * \brief Macro to determine PhysicsFS version program was compiled against. + * + * This macro fills in a PHYSFS_Version structure with the version of the + * library you compiled against. This is determined by what header the + * compiler uses. Note that if you dynamically linked the library, you might + * have a slightly newer or older version at runtime. That version can be + * determined with PHYSFS_getLinkedVersion(), which, unlike PHYSFS_VERSION, + * is not a macro. + * + * \param x A pointer to a PHYSFS_Version struct to initialize. + * + * \sa PHYSFS_Version + * \sa PHYSFS_getLinkedVersion + */ +#define PHYSFS_VERSION(x) \ +{ \ + (x)->major = PHYSFS_VER_MAJOR; \ + (x)->minor = PHYSFS_VER_MINOR; \ + (x)->patch = PHYSFS_VER_PATCH; \ +} + + +/** + * \fn void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) + * \brief Get the version of PhysicsFS that is linked against your program. + * + * If you are using a shared library (DLL) version of PhysFS, then it is + * possible that it will be different than the version you compiled against. + * + * This is a real function; the macro PHYSFS_VERSION tells you what version + * of PhysFS you compiled against: + * + * \code + * PHYSFS_Version compiled; + * PHYSFS_Version linked; + * + * PHYSFS_VERSION(&compiled); + * PHYSFS_getLinkedVersion(&linked); + * printf("We compiled against PhysFS version %d.%d.%d ...\n", + * compiled.major, compiled.minor, compiled.patch); + * printf("But we linked against PhysFS version %d.%d.%d.\n", + * linked.major, linked.minor, linked.patch); + * \endcode + * + * This function may be called safely at any time, even before PHYSFS_init(). + * + * \sa PHYSFS_VERSION + */ +__EXPORT__ void PHYSFS_getLinkedVersion(PHYSFS_Version *ver); + + +/** + * \fn int PHYSFS_init(const char *argv0) + * \brief Initialize the PhysicsFS library. + * + * This must be called before any other PhysicsFS function. + * + * This should be called prior to any attempts to change your process's + * current working directory. + * + * \param argv0 the argv[0] string passed to your program's mainline. + * This may be NULL on most platforms (such as ones without a + * standard main() function), but you should always try to pass + * something in here. Unix-like systems such as Linux _need_ to + * pass argv[0] from main() in here. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_deinit + * \sa PHYSFS_isInit + */ +__EXPORT__ int PHYSFS_init(const char *argv0); + + +/** + * \fn int PHYSFS_deinit(void) + * \brief Deinitialize the PhysicsFS library. + * + * This closes any files opened via PhysicsFS, blanks the search/write paths, + * frees memory, and invalidates all of your file handles. + * + * Note that this call can FAIL if there's a file open for writing that + * refuses to close (for example, the underlying operating system was + * buffering writes to network filesystem, and the fileserver has crashed, + * or a hard drive has failed, etc). It is usually best to close all write + * handles yourself before calling this function, so that you can gracefully + * handle a specific failure. + * + * Once successfully deinitialized, PHYSFS_init() can be called again to + * restart the subsystem. All default API states are restored at this + * point, with the exception of any custom allocator you might have + * specified, which survives between initializations. + * + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is + * undefined, and probably badly screwed up. + * + * \sa PHYSFS_init + * \sa PHYSFS_isInit + */ +__EXPORT__ int PHYSFS_deinit(void); + + +/** + * \fn const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) + * \brief Get a list of supported archive types. + * + * Get a list of archive types supported by this implementation of PhysicFS. + * These are the file formats usable for search path entries. This is for + * informational purposes only. Note that the extension listed is merely + * convention: if we list "ZIP", you can open a PkZip-compatible archive + * with an extension of "XYZ", if you like. + * + * The returned value is an array of pointers to PHYSFS_ArchiveInfo structures, + * with a NULL entry to signify the end of the list: + * + * \code + * PHYSFS_ArchiveInfo **i; + * + * for (i = PHYSFS_supportedArchiveTypes(); *i != NULL; i++) + * { + * printf("Supported archive: [%s], which is [%s].\n", + * (*i)->extension, (*i)->description); + * } + * \endcode + * + * The return values are pointers to static internal memory, and should + * be considered READ ONLY, and never freed. + * + * \return READ ONLY Null-terminated array of READ ONLY structures. + */ +__EXPORT__ const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void); + + +/** + * \fn void PHYSFS_freeList(void *listVar) + * \brief Deallocate resources of lists returned by PhysicsFS. + * + * Certain PhysicsFS functions return lists of information that are + * dynamically allocated. Use this function to free those resources. + * + * \param listVar List of information specified as freeable by this function. + * + * \sa PHYSFS_getCdRomDirs + * \sa PHYSFS_enumerateFiles + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ void PHYSFS_freeList(void *listVar); + + +/** + * \fn const char *PHYSFS_getLastError(void) + * \brief Get human-readable error information. + * + * Get the last PhysicsFS error message as a human-readable, null-terminated + * string. This will be NULL if there's been no error since the last call to + * this function. The pointer returned by this call points to an internal + * buffer. Each thread has a unique error state associated with it, but each + * time a new error message is set, it will overwrite the previous one + * associated with that thread. It is safe to call this function at anytime, + * even before PHYSFS_init(). + * + * It is not wise to expect a specific string of characters here, since the + * error message may be localized into an unfamiliar language. These strings + * are meant to be passed on directly to the user. + * + * \return READ ONLY string of last error message. + */ +__EXPORT__ const char *PHYSFS_getLastError(void); + + +/** + * \fn const char *PHYSFS_getDirSeparator(void) + * \brief Get platform-dependent dir separator string. + * + * This returns "\\" on win32, "/" on Unix, and ":" on MacOS. It may be more + * than one character, depending on the platform, and your code should take + * that into account. Note that this is only useful for setting up the + * search/write paths, since access into those dirs always use '/' + * (platform-independent notation) to separate directories. This is also + * handy for getting platform-independent access when using stdio calls. + * + * \return READ ONLY null-terminated string of platform's dir separator. + */ +__EXPORT__ const char *PHYSFS_getDirSeparator(void); + + +/** + * \fn void PHYSFS_permitSymbolicLinks(int allow) + * \brief Enable or disable following of symbolic links. + * + * Some physical filesystems and archives contain files that are just pointers + * to other files. On the physical filesystem, opening such a link will + * (transparently) open the file that is pointed to. + * + * By default, PhysicsFS will check if a file is really a symlink during open + * calls and fail if it is. Otherwise, the link could take you outside the + * write and search paths, and compromise security. + * + * If you want to take that risk, call this function with a non-zero parameter. + * Note that this is more for sandboxing a program's scripting language, in + * case untrusted scripts try to compromise the system. Generally speaking, + * a user could very well have a legitimate reason to set up a symlink, so + * unless you feel there's a specific danger in allowing them, you should + * permit them. + * + * Symlinks are only explicitly checked when dealing with filenames + * in platform-independent notation. That is, when setting up your + * search and write paths, etc, symlinks are never checked for. + * + * Symbolic link permission can be enabled or disabled at any time after + * you've called PHYSFS_init(), and is disabled by default. + * + * \param allow nonzero to permit symlinks, zero to deny linking. + * + * \sa PHYSFS_symbolicLinksPermitted + */ +__EXPORT__ void PHYSFS_permitSymbolicLinks(int allow); + + +/* !!! FIXME: const this? */ +/** + * \fn char **PHYSFS_getCdRomDirs(void) + * \brief Get an array of paths to available CD-ROM drives. + * + * The dirs returned are platform-dependent ("D:\" on Win32, "/cdrom" or + * whatnot on Unix). Dirs are only returned if there is a disc ready and + * accessible in the drive. So if you've got two drives (D: and E:), and only + * E: has a disc in it, then that's all you get. If the user inserts a disc + * in D: and you call this function again, you get both drives. If, on a + * Unix box, the user unmounts a disc and remounts it elsewhere, the next + * call to this function will reflect that change. + * + * This function refers to "CD-ROM" media, but it really means "inserted disc + * media," such as DVD-ROM, HD-DVD, CDRW, and Blu-Ray discs. It looks for + * filesystems, and as such won't report an audio CD, unless there's a + * mounted filesystem track on it. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **cds = PHYSFS_getCdRomDirs(); + * char **i; + * + * for (i = cds; *i != NULL; i++) + * printf("cdrom dir [%s] is available.\n", *i); + * + * PHYSFS_freeList(cds); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. + * + * \sa PHYSFS_getCdRomDirsCallback + */ +__EXPORT__ char **PHYSFS_getCdRomDirs(void); + + +/** + * \fn const char *PHYSFS_getBaseDir(void) + * \brief Get the path where the application resides. + * + * Helper function. + * + * Get the "base dir". This is the directory where the application was run + * from, which is probably the installation directory, and may or may not + * be the process's current working directory. + * + * You should probably use the base dir in your search path. + * + * \return READ ONLY string of base dir in platform-dependent notation. + * + * \sa PHYSFS_getUserDir + */ +__EXPORT__ const char *PHYSFS_getBaseDir(void); + + +/** + * \fn const char *PHYSFS_getUserDir(void) + * \brief Get the path where user's home directory resides. + * + * Helper function. + * + * Get the "user dir". This is meant to be a suggestion of where a specific + * user of the system can store files. On Unix, this is her home directory. + * On systems with no concept of multiple home directories (MacOS, win95), + * this will default to something like "C:\mybasedir\users\username" + * where "username" will either be the login name, or "default" if the + * platform doesn't support multiple users, either. + * + * You should probably use the user dir as the basis for your write dir, and + * also put it near the beginning of your search path. + * + * \return READ ONLY string of user dir in platform-dependent notation. + * + * \sa PHYSFS_getBaseDir + */ +__EXPORT__ const char *PHYSFS_getUserDir(void); + + +/** + * \fn const char *PHYSFS_getWriteDir(void) + * \brief Get path where PhysicsFS will allow file writing. + * + * Get the current write dir. The default write dir is NULL. + * + * \return READ ONLY string of write dir in platform-dependent notation, + * OR NULL IF NO WRITE PATH IS CURRENTLY SET. + * + * \sa PHYSFS_setWriteDir + */ +__EXPORT__ const char *PHYSFS_getWriteDir(void); + + +/** + * \fn int PHYSFS_setWriteDir(const char *newDir) + * \brief Tell PhysicsFS where it may write files. + * + * Set a new write dir. This will override the previous setting. + * + * This call will fail (and fail to change the write dir) if the current + * write dir still has files open in it. + * + * \param newDir The new directory to be the root of the write dir, + * specified in platform-dependent notation. Setting to NULL + * disables the write dir, so no files can be opened for + * writing via PhysicsFS. + * \return non-zero on success, zero on failure. All attempts to open a file + * for writing via PhysicsFS will fail until this call succeeds. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_getWriteDir + */ +__EXPORT__ int PHYSFS_setWriteDir(const char *newDir); + + +/** + * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * This is a legacy call in PhysicsFS 2.0, equivalent to: + * PHYSFS_mount(newDir, NULL, appendToPath); + * + * You must use this and not PHYSFS_mount if binary compatibility with + * PhysicsFS 1.0 is important (which it may not be for many people). + * + * \sa PHYSFS_mount + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ int PHYSFS_addToSearchPath(const char *newDir, int appendToPath); + + +/** + * \fn int PHYSFS_removeFromSearchPath(const char *oldDir) + * \brief Remove a directory or archive from the search path. + * + * This must be a (case-sensitive) match to a dir or archive already in the + * search path, specified in platform-dependent notation. + * + * This call will fail (and fail to remove from the path) if the element still + * has files open in it. + * + * \param oldDir dir/archive to remove. + * \return nonzero on success, zero on failure. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ int PHYSFS_removeFromSearchPath(const char *oldDir); + + +/** + * \fn char **PHYSFS_getSearchPath(void) + * \brief Get the current search path. + * + * The default search path is an empty list. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **i; + * + * for (i = PHYSFS_getSearchPath(); *i != NULL; i++) + * printf("[%s] is in the search path.\n", *i); + * \endcode + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. NULL if there + * was a problem (read: OUT OF MEMORY). + * + * \sa PHYSFS_getSearchPathCallback + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_removeFromSearchPath + */ +__EXPORT__ char **PHYSFS_getSearchPath(void); + + +/** + * \fn int PHYSFS_setSaneConfig(const char *organization, const char *appName, const char *archiveExt, int includeCdRoms, int archivesFirst) + * \brief Set up sane, default paths. + * + * Helper function. + * + * The write dir will be set to "userdir/.organization/appName", which is + * created if it doesn't exist. + * + * The above is sufficient to make sure your program's configuration directory + * is separated from other clutter, and platform-independent. The period + * before "mygame" even hides the directory on Unix systems. + * + * The search path will be: + * + * - The Write Dir (created if it doesn't exist) + * - The Base Dir (PHYSFS_getBaseDir()) + * - All found CD-ROM dirs (optionally) + * + * These directories are then searched for files ending with the extension + * (archiveExt), which, if they are valid and supported archives, will also + * be added to the search path. If you specified "PKG" for (archiveExt), and + * there's a file named data.PKG in the base dir, it'll be checked. Archives + * can either be appended or prepended to the search path in alphabetical + * order, regardless of which directories they were found in. + * + * All of this can be accomplished from the application, but this just does it + * all for you. Feel free to add more to the search path manually, too. + * + * \param organization Name of your company/group/etc to be used as a + * dirname, so keep it small, and no-frills. + * + * \param appName Program-specific name of your program, to separate it + * from other programs using PhysicsFS. + * + * \param archiveExt File extension used by your program to specify an + * archive. For example, Quake 3 uses "pk3", even though + * they are just zipfiles. Specify NULL to not dig out + * archives automatically. Do not specify the '.' char; + * If you want to look for ZIP files, specify "ZIP" and + * not ".ZIP" ... the archive search is case-insensitive. + * + * \param includeCdRoms Non-zero to include CD-ROMs in the search path, and + * (if (archiveExt) != NULL) search them for archives. + * This may cause a significant amount of blocking + * while discs are accessed, and if there are no discs + * in the drive (or even not mounted on Unix systems), + * then they may not be made available anyhow. You may + * want to specify zero and handle the disc setup + * yourself. + * + * \param archivesFirst Non-zero to prepend the archives to the search path. + * Zero to append them. Ignored if !(archiveExt). + * + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_setSaneConfig(const char *organization, + const char *appName, + const char *archiveExt, + int includeCdRoms, + int archivesFirst); + + +/* Directory management stuff ... */ + +/** + * \fn int PHYSFS_mkdir(const char *dirName) + * \brief Create a directory. + * + * This is specified in platform-independent notation in relation to the + * write dir. All missing parent directories are also created if they + * don't exist. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_mkdir("downloads/maps") then the directories + * "C:\mygame\writedir\downloads" and "C:\mygame\writedir\downloads\maps" + * will be created if possible. If the creation of "maps" fails after we + * have successfully created "downloads", then the function leaves the + * created directory behind and reports failure. + * + * \param dirName New dir to create. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_delete + */ +__EXPORT__ int PHYSFS_mkdir(const char *dirName); + + +/** + * \fn int PHYSFS_delete(const char *filename) + * \brief Delete a file or directory. + * + * (filename) is specified in platform-independent notation in relation to the + * write dir. + * + * A directory must be empty before this call can delete it. + * + * Deleting a symlink will remove the link, not what it points to, regardless + * of whether you "permitSymLinks" or not. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_delete("downloads/maps/level1.map") then the file + * "C:\mygame\writedir\downloads\maps\level1.map" is removed from the + * physical filesystem, if it exists and the operating system permits the + * deletion. + * + * Note that on Unix systems, deleting a file may be successful, but the + * actual file won't be removed until all processes that have an open + * filehandle to it (including your program) close their handles. + * + * Chances are, the bits that make up the file still exist, they are just + * made available to be written over at a later point. Don't consider this + * a security method or anything. :) + * + * \param filename Filename to delete. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_delete(const char *filename); + + +/** + * \fn const char *PHYSFS_getRealDir(const char *filename) + * \brief Figure out where in the search path a file resides. + * + * The file is specified in platform-independent notation. The returned + * filename will be the element of the search path where the file was found, + * which may be a directory, or an archive. Even if there are multiple + * matches in different parts of the search path, only the first one found + * is used, just like when opening a file. + * + * So, if you look for "maps/level1.map", and C:\\mygame is in your search + * path and C:\\mygame\\maps\\level1.map exists, then "C:\mygame" is returned. + * + * If a any part of a match is a symbolic link, and you've not explicitly + * permitted symlinks, then it will be ignored, and the search for a match + * will continue. + * + * If you specify a fake directory that only exists as a mount point, it'll + * be associated with the first archive mounted there, even though that + * directory isn't necessarily contained in a real archive. + * + * \param filename file to look for. + * \return READ ONLY string of element of search path containing the + * the file in question. NULL if not found. + */ +__EXPORT__ const char *PHYSFS_getRealDir(const char *filename); + + +/** + * \fn char **PHYSFS_enumerateFiles(const char *dir) + * \brief Get a file listing of a search path's directory. + * + * Matching directories are interpolated. That is, if "C:\mydir" is in the + * search path and contains a directory "savegames" that contains "x.sav", + * "y.sav", and "z.sav", and there is also a "C:\userdir" in the search path + * that has a "savegames" subdirectory with "w.sav", then the following code: + * + * \code + * char **rc = PHYSFS_enumerateFiles("savegames"); + * char **i; + * + * for (i = rc; *i != NULL; i++) + * printf(" * We've got [%s].\n", *i); + * + * PHYSFS_freeList(rc); + * \endcode + * + * \...will print: + * + * \verbatim + * We've got [x.sav]. + * We've got [y.sav]. + * We've got [z.sav]. + * We've got [w.sav].\endverbatim + * + * Feel free to sort the list however you like. However, the returned data + * will always contain no duplicates, and will be always sorted in alphabetic + * (rather: Unicode) order for you. + * + * Don't forget to call PHYSFS_freeList() with the return value from this + * function when you are done with it. + * + * \param dir directory in platform-independent notation to enumerate. + * \return Null-terminated array of null-terminated strings. + * + * \sa PHYSFS_enumerateFilesCallback + */ +__EXPORT__ char **PHYSFS_enumerateFiles(const char *dir); + + +/** + * \fn int PHYSFS_exists(const char *fname) + * \brief Determine if a file exists in the search path. + * + * Reports true if there is an entry anywhere in the search path by the + * name of (fname). + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists. zero otherwise. + * + * \sa PHYSFS_isDirectory + * \sa PHYSFS_isSymbolicLink + */ +__EXPORT__ int PHYSFS_exists(const char *fname); + + +/** + * \fn int PHYSFS_isDirectory(const char *fname) + * \brief Determine if a file in the search path is really a directory. + * + * Determine if the first occurence of (fname) in the search path is + * really a directory entry. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a directory. zero otherwise. + * + * \sa PHYSFS_exists + * \sa PHYSFS_isSymbolicLink + */ +__EXPORT__ int PHYSFS_isDirectory(const char *fname); + + +/** + * \fn int PHYSFS_isSymbolicLink(const char *fname) + * \brief Determine if a file in the search path is really a symbolic link. + * + * Determine if the first occurence of (fname) in the search path is + * really a symbolic link. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such, + * this function will always return 0 in that case. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a symlink. zero otherwise. + * + * \sa PHYSFS_exists + * \sa PHYSFS_isDirectory + */ +__EXPORT__ int PHYSFS_isSymbolicLink(const char *fname); + + +/** + * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) + * \brief Get the last modification time of a file. + * + * The modtime is returned as a number of seconds since the epoch + * (Jan 1, 1970). The exact derivation and accuracy of this time depends on + * the particular archiver. If there is no reasonable way to obtain this + * information for a particular archiver, or there was some sort of error, + * this function returns (-1). + * + * \param filename filename to check, in platform-independent notation. + * \return last modified time of the file. -1 if it can't be determined. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename); + + +/* i/o stuff... */ + +/** + * \fn PHYSFS_File *PHYSFS_openWrite(const char *filename) + * \brief Open a file for writing. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, it is truncated to + * zero bytes, and the writing offset is set to the start. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openAppend + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openWrite(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openAppend(const char *filename) + * \brief Open a file for appending. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, the writing offset + * is set to the end of the file, so the first write will be the byte after + * the end. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openAppend(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openRead(const char *filename) + * \brief Open a file for reading. + * + * Open a file for reading, in platform-independent notation. The search path + * is checked one at a time until a matching file is found, in which case an + * abstract filehandle is associated with it, and reading may be done. + * The reading offset is set to the first byte of the file. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_read + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openRead(const char *filename); + + +/** + * \fn int PHYSFS_close(PHYSFS_File *handle) + * \brief Close a PhysicsFS filehandle. + * + * This call is capable of failing if the operating system was buffering + * writes to the physical media, and, now forced to write those changes to + * physical media, can not store the data for some reason. In such a case, + * the filehandle stays open. A well-written program should ALWAYS check the + * return value from the close call in addition to every writing call! + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + */ +__EXPORT__ int PHYSFS_close(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Read data from a PhysicsFS filehandle + * + * The file must be opened for reading. + * + * \param handle handle returned from PHYSFS_openRead(). + * \param buffer buffer to store read data into. + * \param objSize size in bytes of objects being read from (handle). + * \param objCount number of (objSize) objects to read from (handle). + * \return number of objects read. PHYSFS_getLastError() can shed light on + * the reason this might be < (objCount), as can PHYSFS_eof(). + * -1 if complete failure. + * + * \sa PHYSFS_eof + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, + void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount); + +/** + * \fn PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Write data to a PhysicsFS filehandle + * + * The file must be opened for writing. + * + * \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend(). + * \param buffer buffer of bytes to write to (handle). + * \param objSize size in bytes of objects being written to (handle). + * \param objCount number of (objSize) objects to write to (handle). + * \return number of objects written. PHYSFS_getLastError() can shed light on + * the reason this might be < (objCount). -1 if complete failure. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, + const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount); + + +/* File position stuff... */ + +/** + * \fn int PHYSFS_eof(PHYSFS_File *handle) + * \brief Check for end-of-file state on a PhysicsFS filehandle. + * + * Determine if the end of file has been reached in a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_openRead(). + * \return nonzero if EOF, zero if not. + * + * \sa PHYSFS_read + * \sa PHYSFS_tell + */ +__EXPORT__ int PHYSFS_eof(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) + * \brief Determine current position within a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_open*(). + * \return offset in bytes from start of file. -1 if error occurred. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_seek + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle); + + +/** + * \fn int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) + * \brief Seek to a new position within a PhysicsFS filehandle. + * + * The next read or write will occur at that place. Seeking past the + * beginning or end of the file is not allowed, and causes an error. + * + * \param handle handle returned from PHYSFS_open*(). + * \param pos number of bytes from start of file to seek to. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_tell + */ +__EXPORT__ int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos); + + +/** + * \fn PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) + * \brief Get total length of a file in bytes. + * + * Note that if the file size can't be determined (since the archive is + * "streamed" or whatnot) than this will report (-1). Also note that if + * another process/thread is writing to this file at the same time, then + * the information this function supplies could be incorrect before you + * get it. Use with caution, or better yet, don't use at all. + * + * \param handle handle returned from PHYSFS_open*(). + * \return size in bytes of the file. -1 if can't be determined. + * + * \sa PHYSFS_tell + * \sa PHYSFS_seek + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle); + + +/* Buffering stuff... */ + +/** + * \fn int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize) + * \brief Set up buffering for a PhysicsFS file handle. + * + * Define an i/o buffer for a file handle. A memory block of (bufsize) bytes + * will be allocated and associated with (handle). + * + * For files opened for reading, up to (bufsize) bytes are read from (handle) + * and stored in the internal buffer. Calls to PHYSFS_read() will pull + * from this buffer until it is empty, and then refill it for more reading. + * Note that compressed files, like ZIP archives, will decompress while + * buffering, so this can be handy for offsetting CPU-intensive operations. + * The buffer isn't filled until you do your next read. + * + * For files opened for writing, data will be buffered to memory until the + * buffer is full or the buffer is flushed. Closing a handle implicitly + * causes a flush...check your return values! + * + * Seeking, etc transparently accounts for buffering. + * + * You can resize an existing buffer by calling this function more than once + * on the same file. Setting the buffer size to zero will free an existing + * buffer. + * + * PhysicsFS file handles are unbuffered by default. + * + * Please check the return value of this function! Failures can include + * not being able to seek backwards in a read-only file when removing the + * buffer, not being able to allocate the buffer, and not being able to + * flush the buffer to disk, among other unexpected problems. + * + * \param handle handle returned from PHYSFS_open*(). + * \param bufsize size, in bytes, of buffer to allocate. + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_flush + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize); + + +/** + * \fn int PHYSFS_flush(PHYSFS_File *handle) + * \brief Flush a buffered PhysicsFS file handle. + * + * For buffered files opened for writing, this will put the current contents + * of the buffer to disk and flag the buffer as empty if possible. + * + * For buffered files opened for reading or unbuffered files, this is a safe + * no-op, and will report success. + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_setBuffer + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_flush(PHYSFS_File *handle); + + +/* Byteorder stuff... */ + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val) + * \brief Swap littleendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val) + * \brief Swap littleendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val) + * \brief Swap littleendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val) + * \brief Swap littleendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val); + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val) + * \brief Swap littleendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val) + * \brief Swap littleendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val); + + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val) + * \brief Swap bigendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val) + * \brief Swap bigendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val) + * \brief Swap bigendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val) + * \brief Swap bigendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val); + + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val) + * \brief Swap bigendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val) + * \brief Swap bigendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit littleendian value. + * + * Convenience function. Read a signed 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit littleendian value. + * + * Convenience function. Read an unsigned 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit bigendian value. + * + * Convenience function. Read a signed 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit bigendian value. + * + * Convenience function. Read an unsigned 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit littleendian value. + * + * Convenience function. Read a signed 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit littleendian value. + * + * Convenience function. Read an unsigned 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit bigendian value. + * + * Convenience function. Read a signed 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit bigendian value. + * + * Convenience function. Read an unsigned 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit littleendian value. + * + * Convenience function. Read a signed 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit littleendian value. + * + * Convenience function. Read an unsigned 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit bigendian value. + * + * Convenience function. Read a signed 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit bigendian value. + * + * Convenience function. Read an unsigned 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit littleendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit littleendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit bigendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit bigendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit littleendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit littleendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit bigendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit bigendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit littleendian value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit littleendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit bigending value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit bigendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/* Everything above this line is part of the PhysicsFS 1.0 API. */ + +/** + * \fn int PHYSFS_isInit(void) + * \brief Determine if the PhysicsFS library is initialized. + * + * Once PHYSFS_init() returns successfully, this will return non-zero. + * Before a successful PHYSFS_init() and after PHYSFS_deinit() returns + * successfully, this will return zero. This function is safe to call at + * any time. + * + * \return non-zero if library is initialized, zero if library is not. + * + * \sa PHYSFS_init + * \sa PHYSFS_deinit + */ +__EXPORT__ int PHYSFS_isInit(void); + + +/** + * \fn int PHYSFS_symbolicLinksPermitted(void) + * \brief Determine if the symbolic links are permitted. + * + * This reports the setting from the last call to PHYSFS_permitSymbolicLinks(). + * If PHYSFS_permitSymbolicLinks() hasn't been called since the library was + * last initialized, symbolic links are implicitly disabled. + * + * \return non-zero if symlinks are permitted, zero if not. + * + * \sa PHYSFS_permitSymbolicLinks + */ +__EXPORT__ int PHYSFS_symbolicLinksPermitted(void); + + +/** + * \struct PHYSFS_Allocator + * \brief PhysicsFS allocation function pointers. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * You create one of these structures for use with PHYSFS_setAllocator. + * Allocators are assumed to be reentrant by the caller; please mutex + * accordingly. + * + * Allocations are always discussed in 64-bits, for future expansion...we're + * on the cusp of a 64-bit transition, and we'll probably be allocating 6 + * gigabytes like it's nothing sooner or later, and I don't want to change + * this again at that point. If you're on a 32-bit platform and have to + * downcast, it's okay to return NULL if the allocation is greater than + * 4 gigabytes, since you'd have to do so anyhow. + * + * \sa PHYSFS_setAllocator + */ +typedef struct PHYSFS_Allocator +{ + int (*Init)(void); /**< Initialize. Can be NULL. Zero on failure. */ + void (*Deinit)(void); /**< Deinitialize your allocator. Can be NULL. */ + void *(*Malloc)(PHYSFS_uint64); /**< Allocate like malloc(). */ + void *(*Realloc)(void *, PHYSFS_uint64); /**< Reallocate like realloc(). */ + void (*Free)(void *); /**< Free memory from Malloc or Realloc. */ +} PHYSFS_Allocator; + + +/** + * \fn int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator) + * \brief Hook your own allocation routines into PhysicsFS. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * By default, PhysicsFS will use whatever is reasonable for a platform + * to manage dynamic memory (usually ANSI C malloc/realloc/calloc/free, but + * some platforms might use something else), but in some uncommon cases, the + * app might want more control over the library's memory management. This + * lets you redirect PhysicsFS to use your own allocation routines instead. + * You can only call this function before PHYSFS_init(); if the library is + * initialized, it'll reject your efforts to change the allocator mid-stream. + * You may call this function after PHYSFS_deinit() if you are willing to + * shut down the library and restart it with a new allocator; this is a safe + * and supported operation. The allocator remains intact between deinit/init + * calls. If you want to return to the platform's default allocator, pass a + * NULL in here. + * + * If you aren't immediately sure what to do with this function, you can + * safely ignore it altogether. + * + * \param allocator Structure containing your allocator's entry points. + * \return zero on failure, non-zero on success. This call only fails + * when used between PHYSFS_init() and PHYSFS_deinit() calls. + */ +__EXPORT__ int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator); + + +/** + * \fn int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * If this is a duplicate, the entry is not added again, even though the + * function succeeds. You may not add the same archive to two different + * mountpoints: duplicate checking is done against the archive and not the + * mountpoint. + * + * When you mount an archive, it is added to a virtual file system...all files + * in all of the archives are interpolated into a single hierachical file + * tree. Two archives mounted at the same place (or an archive with files + * overlapping another mountpoint) may have overlapping files: in such a case, + * the file earliest in the search path is selected, and the other files are + * inaccessible to the application. This allows archives to be used to + * override previous revisions; you can use the mounting mechanism to place + * archives at a specific point in the file tree and prevent overlap; this + * is useful for downloadable mods that might trample over application data + * or each other, for example. + * + * The mountpoint does not need to exist prior to mounting, which is different + * than those familiar with the Unix concept of "mounting" may not expect. + * As well, more than one archive can be mounted to the same mountpoint, or + * mountpoints and archive contents can overlap...the interpolation mechanism + * still functions as usual. + * + * \param newDir directory or archive to add to the path, in + * platform-dependent notation. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, dir + * missing, etc). Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +__EXPORT__ int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath); + +/** + * \fn int PHYSFS_getMountPoint(const char *dir) + * \brief Determine a mounted archive's mountpoint. + * + * You give this function the name of an archive or dir you successfully + * added to the search path, and it reports the location in the interpolated + * tree where it is mounted. Files mounted with a NULL mountpoint or through + * PHYSFS_addToSearchPath() will report "/". The return value is READ ONLY + * and valid until the archive is removed from the search path. + * + * \param dir directory or archive previously added to the path, in + * platform-dependent notation. This must match the string + * used when adding, even if your string would also reference + * the same file with a different string of characters. + * \return READ-ONLY string of mount point if added to path, NULL on failure + * (bogus archive, etc) Specifics of the error can be gleaned from + * PHYSFS_getLastError(). + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +__EXPORT__ const char *PHYSFS_getMountPoint(const char *dir); + + +/** + * \typedef PHYSFS_StringCallback + * \brief Function signature for callbacks that report strings. + * + * These are used to report a list of strings to an original caller, one + * string per callback. All strings are UTF-8 encoded. Functions should not + * try to modify or free the string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promises data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param str The string data about which the callback is meant to inform. + * + * \sa PHYSFS_getCdRomDirsCallback + * \sa PHYSFS_getSearchPathCallback + */ +typedef void (*PHYSFS_StringCallback)(void *data, const char *str); + + +/** + * \typedef PHYSFS_EnumFilesCallback + * \brief Function signature for callbacks that enumerate files. + * + * These are used to report a list of directory entries to an original caller, + * one file/dir/symlink per callback. All strings are UTF-8 encoded. + * Functions should not try to modify or free any string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promises data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param origdir A string containing the full path, in platform-independent + * notation, of the directory containing this file. In most + * cases, this is the directory on which you requested + * enumeration, passed in the callback for your convenience. + * \param fname The filename that is being enumerated. It may not be in + * alphabetical order compared to other callbacks that have + * fired, and it will not contain the full path. You can + * recreate the fullpath with $origdir/$fname ... The file + * can be a subdirectory, a file, a symlink, etc. + * + * \sa PHYSFS_enumerateFilesCallback + */ +typedef void (*PHYSFS_EnumFilesCallback)(void *data, const char *origdir, + const char *fname); + + +/** + * \fn void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate CD-ROM directories, using an application-defined callback. + * + * Internally, PHYSFS_getCdRomDirs() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getCdRomDirs(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * detected disc: + * + * \code + * + * static void foundDisc(void *data, const char *cddir) + * { + * printf("cdrom dir [%s] is available.\n", cddir); + * } + * + * // ... + * PHYSFS_getCdRomDirsCallback(foundDisc, NULL); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * \param c Callback function to notify about detected drives. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getCdRomDirs + */ +__EXPORT__ void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate the search path, using an application-defined callback. + * + * Internally, PHYSFS_getSearchPath() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getSearchPath(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * element of the search path: + * + * \code + * + * static void printSearchPath(void *data, const char *pathItem) + * { + * printf("[%s] is in the search path.\n", pathItem); + * } + * + * // ... + * PHYSFS_getSearchPathCallback(printSearchPath, NULL); + * \endcode + * + * Elements of the search path are reported in order search priority, so the + * first archive/dir that would be examined when looking for a file is the + * first element passed through the callback. + * + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, void *d) + * \brief Get a file listing of a search path's directory, using an application-defined callback. + * + * Internally, PHYSFS_enumerateFiles() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_enumerateFiles(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * element of the search path: + * + * \code + * + * static void printDir(void *data, const char *origdir, const char *fname) + * { + * printf(" * We've got [%s] in [%s].\n", fname, origdir); + * } + * + * // ... + * PHYSFS_enumerateFilesCallback("/some/path", printDir, NULL); + * \endcode + * + * Items sent to the callback are not guaranteed to be in any order whatsoever. + * There is no sorting done at this level, and if you need that, you should + * probably use PHYSFS_enumerateFiles() instead, which guarantees + * alphabetical sorting. This form reports whatever is discovered in each + * archive before moving on to the next. Even within one archive, we can't + * guarantee what order it will discover data. Any sorting you find in + * these callbacks is just pure luck. Do not rely on it. + * + * \param dir Directory, in platform-independent notation, to enumerate. + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_EnumFilesCallback + * \sa PHYSFS_enumerateFiles + */ +__EXPORT__ void PHYSFS_enumerateFilesCallback(const char *dir, + PHYSFS_EnumFilesCallback c, + void *d); + +/** + * \fn void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-4 string to a UTF-8 string. + * + * UCS-4 strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is the same size as the source buffer. UTF-8 + * never uses more than 32-bits per character, so while it may shrink a UCS-4 + * string, it will never expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UCS-4 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-4 string. + * + * UCS-4 strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is four times the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-4 always uses + * four, so an entirely low-ASCII string will quadruple in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-4 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-4 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-2 string to a UTF-8 string. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 never uses more than 32-bits per character, so while it may shrink + * a UCS-2 string, it may also expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that UCS-2 is not UTF-16; we do not support the "surrogate" + * values at this time. + * + * \param src Null-terminated source string in UCS-2 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-2 string. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-2 always uses + * two, so an entirely low-ASCII string will double in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-2 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that UCS-2 is not UTF-16; we do not support the "surrogate" + * values at this time. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-2 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a Latin1 string. + * + * Latin1 strings are 8-bits per character: a popular "high ASCII" + * encoding. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 expands latin1 codepoints over 127 from 1 to 2 bytes, so the string + * may grow in some cases. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that we do not supply a UTF-8 to Latin1 converter, since Latin1 + * can't express most Unicode codepoints. It's a legacy encoding; you should + * be converting away from it at all times. + * + * \param src Null-terminated source string in Latin1 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromLatin1(const char *src, char *dst, + PHYSFS_uint64 len); + +/* Everything above this line is part of the PhysicsFS 2.0 API. */ + + +#ifdef __cplusplus +} +#endif + +#endif /* !defined _INCLUDE_PHYSFS_H_ */ + +/* end of physfs.h ... */ + diff --git a/3rdparty/physfs/physfs_byteorder.c b/3rdparty/physfs/physfs_byteorder.c new file mode 100644 index 0000000..ee3688b --- /dev/null +++ b/3rdparty/physfs/physfs_byteorder.c @@ -0,0 +1,312 @@ +/** + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * Documentation is in physfs.h. It's verbose, honest. :) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#include +#include + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if (defined macintosh) && !(defined __MWERKS__) +#define __inline__ +#endif + +#if (defined _MSC_VER) +#define __inline__ __inline +#endif + +#ifndef PHYSFS_Swap16 +static __inline__ PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D) +{ + return((D<<8)|(D>>8)); +} +#endif +#ifndef PHYSFS_Swap32 +static __inline__ PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D) +{ + return((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24)); +} +#endif +#ifndef PHYSFS_NO_64BIT_SUPPORT +#ifndef PHYSFS_Swap64 +static __inline__ PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) { + PHYSFS_uint32 hi, lo; + + /* Separate into high and low 32-bit values and swap them */ + lo = (PHYSFS_uint32)(val&0xFFFFFFFF); + val >>= 32; + hi = (PHYSFS_uint32)(val&0xFFFFFFFF); + val = PHYSFS_Swap32(lo); + val <<= 32; + val |= PHYSFS_Swap32(hi); + return(val); +} +#endif +#else +#ifndef PHYSFS_Swap64 +/* This is mainly to keep compilers from complaining in PHYSFS code. + If there is no real 64-bit datatype, then compilers will complain about + the fake 64-bit datatype that PHYSFS provides when it compiles user code. +*/ +#define PHYSFS_Swap64(X) (X) +#endif +#endif /* PHYSFS_NO_64BIT_SUPPORT */ + + +/* Byteswap item from the specified endianness to the native endianness */ +#if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN +PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return(x); } +PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return(x); } +PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return(x); } +PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return(x); } +PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return(x); } +PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return(x); } + +PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return(PHYSFS_Swap16(x)); } +PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return(PHYSFS_Swap16(x)); } +PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return(PHYSFS_Swap32(x)); } +PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return(PHYSFS_Swap32(x)); } +PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return(PHYSFS_Swap64(x)); } +PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return(PHYSFS_Swap64(x)); } +#else +PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return(PHYSFS_Swap16(x)); } +PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return(PHYSFS_Swap16(x)); } +PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return(PHYSFS_Swap32(x)); } +PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return(PHYSFS_Swap32(x)); } +PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return(PHYSFS_Swap64(x)); } +PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return(PHYSFS_Swap64(x)); } + +PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return(x); } +PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return(x); } +PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return(x); } +PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return(x); } +PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return(x); } +PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return(x); } +#endif + + +int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val) +{ + PHYSFS_sint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE16(in); + return(1); +} /* PHYSFS_readSLE16 */ + + +int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE16(in); + return(1); +} /* PHYSFS_readULE16 */ + + +int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val) +{ + PHYSFS_sint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE16(in); + return(1); +} /* PHYSFS_readSBE16 */ + + +int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE16(in); + return(1); +} /* PHYSFS_readUBE16 */ + + +int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val) +{ + PHYSFS_sint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE32(in); + return(1); +} /* PHYSFS_readSLE32 */ + + +int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE32(in); + return(1); +} /* PHYSFS_readULE32 */ + + +int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val) +{ + PHYSFS_sint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE32(in); + return(1); +} /* PHYSFS_readSBE32 */ + + +int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE32(in); + return(1); +} /* PHYSFS_readUBE32 */ + + +int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val) +{ + PHYSFS_sint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE64(in); + return(1); +} /* PHYSFS_readSLE64 */ + + +int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val) +{ + PHYSFS_uint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE64(in); + return(1); +} /* PHYSFS_readULE64 */ + + +int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val) +{ + PHYSFS_sint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE64(in); + return(1); +} /* PHYSFS_readSBE64 */ + + +int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val) +{ + PHYSFS_uint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE64(in); + return(1); +} /* PHYSFS_readUBE64 */ + + + +int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val) +{ + PHYSFS_sint16 out = PHYSFS_swapSLE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE16 */ + + +int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val) +{ + PHYSFS_uint16 out = PHYSFS_swapULE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE16 */ + + +int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val) +{ + PHYSFS_sint16 out = PHYSFS_swapSBE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE16 */ + + +int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val) +{ + PHYSFS_uint16 out = PHYSFS_swapUBE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE16 */ + + +int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val) +{ + PHYSFS_sint32 out = PHYSFS_swapSLE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE32 */ + + +int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val) +{ + PHYSFS_uint32 out = PHYSFS_swapULE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE32 */ + + +int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val) +{ + PHYSFS_sint32 out = PHYSFS_swapSBE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE32 */ + + +int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val) +{ + PHYSFS_uint32 out = PHYSFS_swapUBE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE32 */ + + +int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val) +{ + PHYSFS_sint64 out = PHYSFS_swapSLE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE64 */ + + +int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val) +{ + PHYSFS_uint64 out = PHYSFS_swapULE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE64 */ + + +int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val) +{ + PHYSFS_sint64 out = PHYSFS_swapSBE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE64 */ + + +int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val) +{ + PHYSFS_uint64 out = PHYSFS_swapUBE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE64 */ + +/* end of physfs_byteorder.c ... */ + diff --git a/3rdparty/physfs/physfs_casefolding.h b/3rdparty/physfs/physfs_casefolding.h new file mode 100644 index 0000000..0e50f1e --- /dev/null +++ b/3rdparty/physfs/physfs_casefolding.h @@ -0,0 +1,2013 @@ +/* + * This file is part of PhysicsFS (http://icculus.org/physfs/) + * + * This data generated by physfs/extras/makecasefoldhashtable.pl ... + * Do not manually edit this file! + * + * Please see the file LICENSE.txt in the source's root directory. + */ + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +static const CaseFoldMapping case_fold_000[] = { + { 0x0202, 0x0203, 0x0000, 0x0000 }, + { 0x0404, 0x0454, 0x0000, 0x0000 }, + { 0x1E1E, 0x1E1F, 0x0000, 0x0000 }, + { 0x2C2C, 0x2C5C, 0x0000, 0x0000 }, + { 0x10404, 0x1042C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_001[] = { + { 0x0100, 0x0101, 0x0000, 0x0000 }, + { 0x0405, 0x0455, 0x0000, 0x0000 }, + { 0x0504, 0x0505, 0x0000, 0x0000 }, + { 0x2C2D, 0x2C5D, 0x0000, 0x0000 }, + { 0x10405, 0x1042D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_002[] = { + { 0x0200, 0x0201, 0x0000, 0x0000 }, + { 0x0406, 0x0456, 0x0000, 0x0000 }, + { 0x1E1C, 0x1E1D, 0x0000, 0x0000 }, + { 0x1F1D, 0x1F15, 0x0000, 0x0000 }, + { 0x2C2E, 0x2C5E, 0x0000, 0x0000 }, + { 0x10406, 0x1042E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_003[] = { + { 0x0102, 0x0103, 0x0000, 0x0000 }, + { 0x0407, 0x0457, 0x0000, 0x0000 }, + { 0x0506, 0x0507, 0x0000, 0x0000 }, + { 0x1F1C, 0x1F14, 0x0000, 0x0000 }, + { 0x10407, 0x1042F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_004[] = { + { 0x0206, 0x0207, 0x0000, 0x0000 }, + { 0x0400, 0x0450, 0x0000, 0x0000 }, + { 0x1E1A, 0x1E1B, 0x0000, 0x0000 }, + { 0x1F1B, 0x1F13, 0x0000, 0x0000 }, + { 0x2C28, 0x2C58, 0x0000, 0x0000 }, + { 0x10400, 0x10428, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_005[] = { + { 0x0104, 0x0105, 0x0000, 0x0000 }, + { 0x0401, 0x0451, 0x0000, 0x0000 }, + { 0x0500, 0x0501, 0x0000, 0x0000 }, + { 0x1F1A, 0x1F12, 0x0000, 0x0000 }, + { 0x2C29, 0x2C59, 0x0000, 0x0000 }, + { 0x10401, 0x10429, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_006[] = { + { 0x0204, 0x0205, 0x0000, 0x0000 }, + { 0x0402, 0x0452, 0x0000, 0x0000 }, + { 0x1E18, 0x1E19, 0x0000, 0x0000 }, + { 0x1F19, 0x1F11, 0x0000, 0x0000 }, + { 0x2C2A, 0x2C5A, 0x0000, 0x0000 }, + { 0x10402, 0x1042A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_007[] = { + { 0x0106, 0x0107, 0x0000, 0x0000 }, + { 0x0403, 0x0453, 0x0000, 0x0000 }, + { 0x0502, 0x0503, 0x0000, 0x0000 }, + { 0x1F18, 0x1F10, 0x0000, 0x0000 }, + { 0x2126, 0x03C9, 0x0000, 0x0000 }, + { 0x2C2B, 0x2C5B, 0x0000, 0x0000 }, + { 0x10403, 0x1042B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_008[] = { + { 0x020A, 0x020B, 0x0000, 0x0000 }, + { 0x040C, 0x045C, 0x0000, 0x0000 }, + { 0x1E16, 0x1E17, 0x0000, 0x0000 }, + { 0x2C24, 0x2C54, 0x0000, 0x0000 }, + { 0x1040C, 0x10434, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_009[] = { + { 0x0108, 0x0109, 0x0000, 0x0000 }, + { 0x040D, 0x045D, 0x0000, 0x0000 }, + { 0x050C, 0x050D, 0x0000, 0x0000 }, + { 0x2C25, 0x2C55, 0x0000, 0x0000 }, + { 0x1040D, 0x10435, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_010[] = { + { 0x0208, 0x0209, 0x0000, 0x0000 }, + { 0x040E, 0x045E, 0x0000, 0x0000 }, + { 0x1E14, 0x1E15, 0x0000, 0x0000 }, + { 0x212B, 0x00E5, 0x0000, 0x0000 }, + { 0x2C26, 0x2C56, 0x0000, 0x0000 }, + { 0x1040E, 0x10436, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_011[] = { + { 0x010A, 0x010B, 0x0000, 0x0000 }, + { 0x040F, 0x045F, 0x0000, 0x0000 }, + { 0x050E, 0x050F, 0x0000, 0x0000 }, + { 0x212A, 0x006B, 0x0000, 0x0000 }, + { 0x2C27, 0x2C57, 0x0000, 0x0000 }, + { 0x1040F, 0x10437, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_012[] = { + { 0x020E, 0x020F, 0x0000, 0x0000 }, + { 0x0408, 0x0458, 0x0000, 0x0000 }, + { 0x1E12, 0x1E13, 0x0000, 0x0000 }, + { 0x2C20, 0x2C50, 0x0000, 0x0000 }, + { 0x10408, 0x10430, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_013[] = { + { 0x010C, 0x010D, 0x0000, 0x0000 }, + { 0x0409, 0x0459, 0x0000, 0x0000 }, + { 0x0508, 0x0509, 0x0000, 0x0000 }, + { 0x2C21, 0x2C51, 0x0000, 0x0000 }, + { 0x10409, 0x10431, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_014[] = { + { 0x020C, 0x020D, 0x0000, 0x0000 }, + { 0x040A, 0x045A, 0x0000, 0x0000 }, + { 0x1E10, 0x1E11, 0x0000, 0x0000 }, + { 0x2C22, 0x2C52, 0x0000, 0x0000 }, + { 0x1040A, 0x10432, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_015[] = { + { 0x010E, 0x010F, 0x0000, 0x0000 }, + { 0x040B, 0x045B, 0x0000, 0x0000 }, + { 0x050A, 0x050B, 0x0000, 0x0000 }, + { 0x2C23, 0x2C53, 0x0000, 0x0000 }, + { 0x1040B, 0x10433, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_016[] = { + { 0x0212, 0x0213, 0x0000, 0x0000 }, + { 0x0414, 0x0434, 0x0000, 0x0000 }, + { 0x1E0E, 0x1E0F, 0x0000, 0x0000 }, + { 0x1F0F, 0x1F07, 0x0000, 0x0000 }, + { 0x10414, 0x1043C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_017[] = { + { 0x0110, 0x0111, 0x0000, 0x0000 }, + { 0x0415, 0x0435, 0x0000, 0x0000 }, + { 0x1F0E, 0x1F06, 0x0000, 0x0000 }, + { 0x10415, 0x1043D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_018[] = { + { 0x0210, 0x0211, 0x0000, 0x0000 }, + { 0x0416, 0x0436, 0x0000, 0x0000 }, + { 0x1E0C, 0x1E0D, 0x0000, 0x0000 }, + { 0x1F0D, 0x1F05, 0x0000, 0x0000 }, + { 0x10416, 0x1043E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_019[] = { + { 0x0112, 0x0113, 0x0000, 0x0000 }, + { 0x0417, 0x0437, 0x0000, 0x0000 }, + { 0x1F0C, 0x1F04, 0x0000, 0x0000 }, + { 0x10417, 0x1043F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_020[] = { + { 0x0216, 0x0217, 0x0000, 0x0000 }, + { 0x0410, 0x0430, 0x0000, 0x0000 }, + { 0x1E0A, 0x1E0B, 0x0000, 0x0000 }, + { 0x1F0B, 0x1F03, 0x0000, 0x0000 }, + { 0x10410, 0x10438, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_021[] = { + { 0x0114, 0x0115, 0x0000, 0x0000 }, + { 0x0411, 0x0431, 0x0000, 0x0000 }, + { 0x1F0A, 0x1F02, 0x0000, 0x0000 }, + { 0x10411, 0x10439, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_022[] = { + { 0x0214, 0x0215, 0x0000, 0x0000 }, + { 0x0412, 0x0432, 0x0000, 0x0000 }, + { 0x1E08, 0x1E09, 0x0000, 0x0000 }, + { 0x1F09, 0x1F01, 0x0000, 0x0000 }, + { 0x10412, 0x1043A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_023[] = { + { 0x0116, 0x0117, 0x0000, 0x0000 }, + { 0x0413, 0x0433, 0x0000, 0x0000 }, + { 0x1F08, 0x1F00, 0x0000, 0x0000 }, + { 0x10413, 0x1043B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_024[] = { + { 0x021A, 0x021B, 0x0000, 0x0000 }, + { 0x041C, 0x043C, 0x0000, 0x0000 }, + { 0x1E06, 0x1E07, 0x0000, 0x0000 }, + { 0x1041C, 0x10444, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_025[] = { + { 0x0118, 0x0119, 0x0000, 0x0000 }, + { 0x041D, 0x043D, 0x0000, 0x0000 }, + { 0x1041D, 0x10445, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_026[] = { + { 0x0218, 0x0219, 0x0000, 0x0000 }, + { 0x041E, 0x043E, 0x0000, 0x0000 }, + { 0x1E04, 0x1E05, 0x0000, 0x0000 }, + { 0x1041E, 0x10446, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_027[] = { + { 0x011A, 0x011B, 0x0000, 0x0000 }, + { 0x041F, 0x043F, 0x0000, 0x0000 }, + { 0x1041F, 0x10447, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_028[] = { + { 0x021E, 0x021F, 0x0000, 0x0000 }, + { 0x0418, 0x0438, 0x0000, 0x0000 }, + { 0x1E02, 0x1E03, 0x0000, 0x0000 }, + { 0x10418, 0x10440, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_029[] = { + { 0x011C, 0x011D, 0x0000, 0x0000 }, + { 0x0419, 0x0439, 0x0000, 0x0000 }, + { 0x10419, 0x10441, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_030[] = { + { 0x021C, 0x021D, 0x0000, 0x0000 }, + { 0x041A, 0x043A, 0x0000, 0x0000 }, + { 0x1E00, 0x1E01, 0x0000, 0x0000 }, + { 0x1041A, 0x10442, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_031[] = { + { 0x011E, 0x011F, 0x0000, 0x0000 }, + { 0x041B, 0x043B, 0x0000, 0x0000 }, + { 0x1041B, 0x10443, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_032[] = { + { 0x0222, 0x0223, 0x0000, 0x0000 }, + { 0x0424, 0x0444, 0x0000, 0x0000 }, + { 0x1E3E, 0x1E3F, 0x0000, 0x0000 }, + { 0x1F3F, 0x1F37, 0x0000, 0x0000 }, + { 0x2C0C, 0x2C3C, 0x0000, 0x0000 }, + { 0x10424, 0x1044C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_033[] = { + { 0x0120, 0x0121, 0x0000, 0x0000 }, + { 0x0425, 0x0445, 0x0000, 0x0000 }, + { 0x1F3E, 0x1F36, 0x0000, 0x0000 }, + { 0x2C0D, 0x2C3D, 0x0000, 0x0000 }, + { 0x10425, 0x1044D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_034[] = { + { 0x0220, 0x019E, 0x0000, 0x0000 }, + { 0x0426, 0x0446, 0x0000, 0x0000 }, + { 0x1E3C, 0x1E3D, 0x0000, 0x0000 }, + { 0x1F3D, 0x1F35, 0x0000, 0x0000 }, + { 0x2C0E, 0x2C3E, 0x0000, 0x0000 }, + { 0x10426, 0x1044E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_035[] = { + { 0x0122, 0x0123, 0x0000, 0x0000 }, + { 0x0427, 0x0447, 0x0000, 0x0000 }, + { 0x1F3C, 0x1F34, 0x0000, 0x0000 }, + { 0x2C0F, 0x2C3F, 0x0000, 0x0000 }, + { 0x10427, 0x1044F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_036[] = { + { 0x0226, 0x0227, 0x0000, 0x0000 }, + { 0x0420, 0x0440, 0x0000, 0x0000 }, + { 0x1E3A, 0x1E3B, 0x0000, 0x0000 }, + { 0x1F3B, 0x1F33, 0x0000, 0x0000 }, + { 0x2C08, 0x2C38, 0x0000, 0x0000 }, + { 0x10420, 0x10448, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_037[] = { + { 0x0124, 0x0125, 0x0000, 0x0000 }, + { 0x0421, 0x0441, 0x0000, 0x0000 }, + { 0x1F3A, 0x1F32, 0x0000, 0x0000 }, + { 0x2C09, 0x2C39, 0x0000, 0x0000 }, + { 0x10421, 0x10449, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_038[] = { + { 0x0224, 0x0225, 0x0000, 0x0000 }, + { 0x0422, 0x0442, 0x0000, 0x0000 }, + { 0x1E38, 0x1E39, 0x0000, 0x0000 }, + { 0x1F39, 0x1F31, 0x0000, 0x0000 }, + { 0x2C0A, 0x2C3A, 0x0000, 0x0000 }, + { 0x10422, 0x1044A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_039[] = { + { 0x0126, 0x0127, 0x0000, 0x0000 }, + { 0x0423, 0x0443, 0x0000, 0x0000 }, + { 0x1F38, 0x1F30, 0x0000, 0x0000 }, + { 0x2C0B, 0x2C3B, 0x0000, 0x0000 }, + { 0x10423, 0x1044B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_040[] = { + { 0x022A, 0x022B, 0x0000, 0x0000 }, + { 0x042C, 0x044C, 0x0000, 0x0000 }, + { 0x1E36, 0x1E37, 0x0000, 0x0000 }, + { 0x2C04, 0x2C34, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_041[] = { + { 0x0128, 0x0129, 0x0000, 0x0000 }, + { 0x042D, 0x044D, 0x0000, 0x0000 }, + { 0x2C05, 0x2C35, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_042[] = { + { 0x0228, 0x0229, 0x0000, 0x0000 }, + { 0x042E, 0x044E, 0x0000, 0x0000 }, + { 0x1E34, 0x1E35, 0x0000, 0x0000 }, + { 0x2C06, 0x2C36, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_043[] = { + { 0x012A, 0x012B, 0x0000, 0x0000 }, + { 0x042F, 0x044F, 0x0000, 0x0000 }, + { 0x2C07, 0x2C37, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_044[] = { + { 0x022E, 0x022F, 0x0000, 0x0000 }, + { 0x0428, 0x0448, 0x0000, 0x0000 }, + { 0x1E32, 0x1E33, 0x0000, 0x0000 }, + { 0x2C00, 0x2C30, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_045[] = { + { 0x012C, 0x012D, 0x0000, 0x0000 }, + { 0x0429, 0x0449, 0x0000, 0x0000 }, + { 0x2C01, 0x2C31, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_046[] = { + { 0x022C, 0x022D, 0x0000, 0x0000 }, + { 0x042A, 0x044A, 0x0000, 0x0000 }, + { 0x1E30, 0x1E31, 0x0000, 0x0000 }, + { 0x2C02, 0x2C32, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_047[] = { + { 0x012E, 0x012F, 0x0000, 0x0000 }, + { 0x042B, 0x044B, 0x0000, 0x0000 }, + { 0x2C03, 0x2C33, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_048[] = { + { 0x0232, 0x0233, 0x0000, 0x0000 }, + { 0x0535, 0x0565, 0x0000, 0x0000 }, + { 0x1E2E, 0x1E2F, 0x0000, 0x0000 }, + { 0x1F2F, 0x1F27, 0x0000, 0x0000 }, + { 0x2C1C, 0x2C4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_049[] = { + { 0x0130, 0x0069, 0x0307, 0x0000 }, + { 0x0534, 0x0564, 0x0000, 0x0000 }, + { 0x1F2E, 0x1F26, 0x0000, 0x0000 }, + { 0x2C1D, 0x2C4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_050[] = { + { 0x0230, 0x0231, 0x0000, 0x0000 }, + { 0x0537, 0x0567, 0x0000, 0x0000 }, + { 0x1E2C, 0x1E2D, 0x0000, 0x0000 }, + { 0x1F2D, 0x1F25, 0x0000, 0x0000 }, + { 0x2C1E, 0x2C4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_051[] = { + { 0x0132, 0x0133, 0x0000, 0x0000 }, + { 0x0536, 0x0566, 0x0000, 0x0000 }, + { 0x1F2C, 0x1F24, 0x0000, 0x0000 }, + { 0x2C1F, 0x2C4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_052[] = { + { 0x0531, 0x0561, 0x0000, 0x0000 }, + { 0x1E2A, 0x1E2B, 0x0000, 0x0000 }, + { 0x1F2B, 0x1F23, 0x0000, 0x0000 }, + { 0x2C18, 0x2C48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_053[] = { + { 0x0134, 0x0135, 0x0000, 0x0000 }, + { 0x1F2A, 0x1F22, 0x0000, 0x0000 }, + { 0x2C19, 0x2C49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_054[] = { + { 0x0533, 0x0563, 0x0000, 0x0000 }, + { 0x1E28, 0x1E29, 0x0000, 0x0000 }, + { 0x1F29, 0x1F21, 0x0000, 0x0000 }, + { 0x2C1A, 0x2C4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_055[] = { + { 0x0136, 0x0137, 0x0000, 0x0000 }, + { 0x0532, 0x0562, 0x0000, 0x0000 }, + { 0x1F28, 0x1F20, 0x0000, 0x0000 }, + { 0x2C1B, 0x2C4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_056[] = { + { 0x0139, 0x013A, 0x0000, 0x0000 }, + { 0x053D, 0x056D, 0x0000, 0x0000 }, + { 0x1E26, 0x1E27, 0x0000, 0x0000 }, + { 0x2C14, 0x2C44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_057[] = { + { 0x023B, 0x023C, 0x0000, 0x0000 }, + { 0x053C, 0x056C, 0x0000, 0x0000 }, + { 0x2C15, 0x2C45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_058[] = { + { 0x013B, 0x013C, 0x0000, 0x0000 }, + { 0x053F, 0x056F, 0x0000, 0x0000 }, + { 0x1E24, 0x1E25, 0x0000, 0x0000 }, + { 0x2C16, 0x2C46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_059[] = { + { 0x053E, 0x056E, 0x0000, 0x0000 }, + { 0x2C17, 0x2C47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_060[] = { + { 0x013D, 0x013E, 0x0000, 0x0000 }, + { 0x0539, 0x0569, 0x0000, 0x0000 }, + { 0x1E22, 0x1E23, 0x0000, 0x0000 }, + { 0x2C10, 0x2C40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_061[] = { + { 0x0538, 0x0568, 0x0000, 0x0000 }, + { 0x2C11, 0x2C41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_062[] = { + { 0x013F, 0x0140, 0x0000, 0x0000 }, + { 0x053B, 0x056B, 0x0000, 0x0000 }, + { 0x1E20, 0x1E21, 0x0000, 0x0000 }, + { 0x2C12, 0x2C42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_063[] = { + { 0x023D, 0x019A, 0x0000, 0x0000 }, + { 0x053A, 0x056A, 0x0000, 0x0000 }, + { 0x2C13, 0x2C43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_064[] = { + { 0x0141, 0x0142, 0x0000, 0x0000 }, + { 0x0545, 0x0575, 0x0000, 0x0000 }, + { 0x1E5E, 0x1E5F, 0x0000, 0x0000 }, + { 0x1F5F, 0x1F57, 0x0000, 0x0000 }, + { 0x2161, 0x2171, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_065[] = { + { 0x0041, 0x0061, 0x0000, 0x0000 }, + { 0x0544, 0x0574, 0x0000, 0x0000 }, + { 0x2160, 0x2170, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_066[] = { + { 0x0042, 0x0062, 0x0000, 0x0000 }, + { 0x0143, 0x0144, 0x0000, 0x0000 }, + { 0x0547, 0x0577, 0x0000, 0x0000 }, + { 0x1E5C, 0x1E5D, 0x0000, 0x0000 }, + { 0x1F5D, 0x1F55, 0x0000, 0x0000 }, + { 0x2163, 0x2173, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_067[] = { + { 0x0043, 0x0063, 0x0000, 0x0000 }, + { 0x0241, 0x0294, 0x0000, 0x0000 }, + { 0x0546, 0x0576, 0x0000, 0x0000 }, + { 0x2162, 0x2172, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_068[] = { + { 0x0044, 0x0064, 0x0000, 0x0000 }, + { 0x0145, 0x0146, 0x0000, 0x0000 }, + { 0x0541, 0x0571, 0x0000, 0x0000 }, + { 0x1E5A, 0x1E5B, 0x0000, 0x0000 }, + { 0x1F5B, 0x1F53, 0x0000, 0x0000 }, + { 0x2165, 0x2175, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_069[] = { + { 0x0045, 0x0065, 0x0000, 0x0000 }, + { 0x0540, 0x0570, 0x0000, 0x0000 }, + { 0x2164, 0x2174, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_070[] = { + { 0x0046, 0x0066, 0x0000, 0x0000 }, + { 0x0147, 0x0148, 0x0000, 0x0000 }, + { 0x0345, 0x03B9, 0x0000, 0x0000 }, + { 0x0543, 0x0573, 0x0000, 0x0000 }, + { 0x1E58, 0x1E59, 0x0000, 0x0000 }, + { 0x1F59, 0x1F51, 0x0000, 0x0000 }, + { 0x2167, 0x2177, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_071[] = { + { 0x0047, 0x0067, 0x0000, 0x0000 }, + { 0x0542, 0x0572, 0x0000, 0x0000 }, + { 0x2166, 0x2176, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_072[] = { + { 0x0048, 0x0068, 0x0000, 0x0000 }, + { 0x0149, 0x02BC, 0x006E, 0x0000 }, + { 0x054D, 0x057D, 0x0000, 0x0000 }, + { 0x1E56, 0x1E57, 0x0000, 0x0000 }, + { 0x2169, 0x2179, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_073[] = { + { 0x0049, 0x0069, 0x0000, 0x0000 }, + { 0x054C, 0x057C, 0x0000, 0x0000 }, + { 0x1F56, 0x03C5, 0x0313, 0x0342 }, + { 0x2168, 0x2178, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_074[] = { + { 0x004A, 0x006A, 0x0000, 0x0000 }, + { 0x054F, 0x057F, 0x0000, 0x0000 }, + { 0x1E54, 0x1E55, 0x0000, 0x0000 }, + { 0x216B, 0x217B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_075[] = { + { 0x004B, 0x006B, 0x0000, 0x0000 }, + { 0x014A, 0x014B, 0x0000, 0x0000 }, + { 0x054E, 0x057E, 0x0000, 0x0000 }, + { 0x1F54, 0x03C5, 0x0313, 0x0301 }, + { 0x216A, 0x217A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_076[] = { + { 0x004C, 0x006C, 0x0000, 0x0000 }, + { 0x0549, 0x0579, 0x0000, 0x0000 }, + { 0x1E52, 0x1E53, 0x0000, 0x0000 }, + { 0x216D, 0x217D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_077[] = { + { 0x004D, 0x006D, 0x0000, 0x0000 }, + { 0x014C, 0x014D, 0x0000, 0x0000 }, + { 0x0548, 0x0578, 0x0000, 0x0000 }, + { 0x1F52, 0x03C5, 0x0313, 0x0300 }, + { 0x216C, 0x217C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_078[] = { + { 0x004E, 0x006E, 0x0000, 0x0000 }, + { 0x054B, 0x057B, 0x0000, 0x0000 }, + { 0x1E50, 0x1E51, 0x0000, 0x0000 }, + { 0x216F, 0x217F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_079[] = { + { 0x004F, 0x006F, 0x0000, 0x0000 }, + { 0x014E, 0x014F, 0x0000, 0x0000 }, + { 0x054A, 0x057A, 0x0000, 0x0000 }, + { 0x1F50, 0x03C5, 0x0313, 0x0000 }, + { 0x216E, 0x217E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_080[] = { + { 0x0050, 0x0070, 0x0000, 0x0000 }, + { 0x0555, 0x0585, 0x0000, 0x0000 }, + { 0x1E4E, 0x1E4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_081[] = { + { 0x0051, 0x0071, 0x0000, 0x0000 }, + { 0x0150, 0x0151, 0x0000, 0x0000 }, + { 0x0554, 0x0584, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_082[] = { + { 0x0052, 0x0072, 0x0000, 0x0000 }, + { 0x1E4C, 0x1E4D, 0x0000, 0x0000 }, + { 0x1F4D, 0x1F45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_083[] = { + { 0x0053, 0x0073, 0x0000, 0x0000 }, + { 0x0152, 0x0153, 0x0000, 0x0000 }, + { 0x0556, 0x0586, 0x0000, 0x0000 }, + { 0x1F4C, 0x1F44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_084[] = { + { 0x0054, 0x0074, 0x0000, 0x0000 }, + { 0x0551, 0x0581, 0x0000, 0x0000 }, + { 0x1E4A, 0x1E4B, 0x0000, 0x0000 }, + { 0x1F4B, 0x1F43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_085[] = { + { 0x0055, 0x0075, 0x0000, 0x0000 }, + { 0x0154, 0x0155, 0x0000, 0x0000 }, + { 0x0550, 0x0580, 0x0000, 0x0000 }, + { 0x1F4A, 0x1F42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_086[] = { + { 0x0056, 0x0076, 0x0000, 0x0000 }, + { 0x0553, 0x0583, 0x0000, 0x0000 }, + { 0x1E48, 0x1E49, 0x0000, 0x0000 }, + { 0x1F49, 0x1F41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_087[] = { + { 0x0057, 0x0077, 0x0000, 0x0000 }, + { 0x0156, 0x0157, 0x0000, 0x0000 }, + { 0x0552, 0x0582, 0x0000, 0x0000 }, + { 0x1F48, 0x1F40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_088[] = { + { 0x0058, 0x0078, 0x0000, 0x0000 }, + { 0x1E46, 0x1E47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_089[] = { + { 0x0059, 0x0079, 0x0000, 0x0000 }, + { 0x0158, 0x0159, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_090[] = { + { 0x005A, 0x007A, 0x0000, 0x0000 }, + { 0x1E44, 0x1E45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_091[] = { + { 0x015A, 0x015B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_092[] = { + { 0x1E42, 0x1E43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_093[] = { + { 0x015C, 0x015D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_094[] = { + { 0x1E40, 0x1E41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_095[] = { + { 0x015E, 0x015F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_096[] = { + { 0x0464, 0x0465, 0x0000, 0x0000 }, + { 0x1E7E, 0x1E7F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_097[] = { + { 0x0160, 0x0161, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_098[] = { + { 0x0466, 0x0467, 0x0000, 0x0000 }, + { 0x1E7C, 0x1E7D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_099[] = { + { 0x0162, 0x0163, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_100[] = { + { 0x0460, 0x0461, 0x0000, 0x0000 }, + { 0x1E7A, 0x1E7B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_101[] = { + { 0x0164, 0x0165, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_102[] = { + { 0x0462, 0x0463, 0x0000, 0x0000 }, + { 0x1E78, 0x1E79, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_103[] = { + { 0x0166, 0x0167, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_104[] = { + { 0x046C, 0x046D, 0x0000, 0x0000 }, + { 0x1E76, 0x1E77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_105[] = { + { 0x0168, 0x0169, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_106[] = { + { 0x046E, 0x046F, 0x0000, 0x0000 }, + { 0x1E74, 0x1E75, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_107[] = { + { 0x016A, 0x016B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_108[] = { + { 0x0468, 0x0469, 0x0000, 0x0000 }, + { 0x1E72, 0x1E73, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_109[] = { + { 0x016C, 0x016D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_110[] = { + { 0x046A, 0x046B, 0x0000, 0x0000 }, + { 0x1E70, 0x1E71, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_111[] = { + { 0x016E, 0x016F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_112[] = { + { 0x0474, 0x0475, 0x0000, 0x0000 }, + { 0x1E6E, 0x1E6F, 0x0000, 0x0000 }, + { 0x1F6F, 0x1F67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_113[] = { + { 0x0170, 0x0171, 0x0000, 0x0000 }, + { 0x1F6E, 0x1F66, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_114[] = { + { 0x0476, 0x0477, 0x0000, 0x0000 }, + { 0x1E6C, 0x1E6D, 0x0000, 0x0000 }, + { 0x1F6D, 0x1F65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_115[] = { + { 0x0172, 0x0173, 0x0000, 0x0000 }, + { 0x1F6C, 0x1F64, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_116[] = { + { 0x0470, 0x0471, 0x0000, 0x0000 }, + { 0x1E6A, 0x1E6B, 0x0000, 0x0000 }, + { 0x1F6B, 0x1F63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_117[] = { + { 0x0174, 0x0175, 0x0000, 0x0000 }, + { 0x1F6A, 0x1F62, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_118[] = { + { 0x0472, 0x0473, 0x0000, 0x0000 }, + { 0x1E68, 0x1E69, 0x0000, 0x0000 }, + { 0x1F69, 0x1F61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_119[] = { + { 0x0176, 0x0177, 0x0000, 0x0000 }, + { 0x1F68, 0x1F60, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_120[] = { + { 0x0179, 0x017A, 0x0000, 0x0000 }, + { 0x047C, 0x047D, 0x0000, 0x0000 }, + { 0x1E66, 0x1E67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_121[] = { + { 0x0178, 0x00FF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_122[] = { + { 0x017B, 0x017C, 0x0000, 0x0000 }, + { 0x047E, 0x047F, 0x0000, 0x0000 }, + { 0x1E64, 0x1E65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_124[] = { + { 0x017D, 0x017E, 0x0000, 0x0000 }, + { 0x0478, 0x0479, 0x0000, 0x0000 }, + { 0x1E62, 0x1E63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_126[] = { + { 0x017F, 0x0073, 0x0000, 0x0000 }, + { 0x047A, 0x047B, 0x0000, 0x0000 }, + { 0x1E60, 0x1E61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_128[] = { + { 0x0181, 0x0253, 0x0000, 0x0000 }, + { 0x1F9F, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CAC, 0x2CAD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_129[] = { + { 0x1F9E, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_130[] = { + { 0x0587, 0x0565, 0x0582, 0x0000 }, + { 0x1F9D, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CAE, 0x2CAF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_131[] = { + { 0x0182, 0x0183, 0x0000, 0x0000 }, + { 0x1F9C, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_132[] = { + { 0x0480, 0x0481, 0x0000, 0x0000 }, + { 0x1E9A, 0x0061, 0x02BE, 0x0000 }, + { 0x1F9B, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA8, 0x2CA9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_133[] = { + { 0x0184, 0x0185, 0x0000, 0x0000 }, + { 0x0386, 0x03AC, 0x0000, 0x0000 }, + { 0x1E9B, 0x1E61, 0x0000, 0x0000 }, + { 0x1F9A, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_134[] = { + { 0x0187, 0x0188, 0x0000, 0x0000 }, + { 0x1E98, 0x0077, 0x030A, 0x0000 }, + { 0x1F99, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CAA, 0x2CAB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_135[] = { + { 0x0186, 0x0254, 0x0000, 0x0000 }, + { 0x1E99, 0x0079, 0x030A, 0x0000 }, + { 0x1F98, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_136[] = { + { 0x0189, 0x0256, 0x0000, 0x0000 }, + { 0x048C, 0x048D, 0x0000, 0x0000 }, + { 0x1E96, 0x0068, 0x0331, 0x0000 }, + { 0x1F97, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CA4, 0x2CA5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_137[] = { + { 0x038A, 0x03AF, 0x0000, 0x0000 }, + { 0x1E97, 0x0074, 0x0308, 0x0000 }, + { 0x1F96, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_138[] = { + { 0x018B, 0x018C, 0x0000, 0x0000 }, + { 0x0389, 0x03AE, 0x0000, 0x0000 }, + { 0x048E, 0x048F, 0x0000, 0x0000 }, + { 0x1E94, 0x1E95, 0x0000, 0x0000 }, + { 0x1F95, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CA6, 0x2CA7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_139[] = { + { 0x018A, 0x0257, 0x0000, 0x0000 }, + { 0x0388, 0x03AD, 0x0000, 0x0000 }, + { 0x1F94, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_140[] = { + { 0x038F, 0x03CE, 0x0000, 0x0000 }, + { 0x1E92, 0x1E93, 0x0000, 0x0000 }, + { 0x1F93, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA0, 0x2CA1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_141[] = { + { 0x038E, 0x03CD, 0x0000, 0x0000 }, + { 0x1F92, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_142[] = { + { 0x018F, 0x0259, 0x0000, 0x0000 }, + { 0x048A, 0x048B, 0x0000, 0x0000 }, + { 0x1E90, 0x1E91, 0x0000, 0x0000 }, + { 0x1F91, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CA2, 0x2CA3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_143[] = { + { 0x018E, 0x01DD, 0x0000, 0x0000 }, + { 0x038C, 0x03CC, 0x0000, 0x0000 }, + { 0x1F90, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_144[] = { + { 0x0191, 0x0192, 0x0000, 0x0000 }, + { 0x0393, 0x03B3, 0x0000, 0x0000 }, + { 0x0494, 0x0495, 0x0000, 0x0000 }, + { 0x1E8E, 0x1E8F, 0x0000, 0x0000 }, + { 0x1F8F, 0x1F07, 0x03B9, 0x0000 }, + { 0x2CBC, 0x2CBD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_145[] = { + { 0x0190, 0x025B, 0x0000, 0x0000 }, + { 0x0392, 0x03B2, 0x0000, 0x0000 }, + { 0x1F8E, 0x1F06, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_146[] = { + { 0x0193, 0x0260, 0x0000, 0x0000 }, + { 0x0391, 0x03B1, 0x0000, 0x0000 }, + { 0x0496, 0x0497, 0x0000, 0x0000 }, + { 0x1E8C, 0x1E8D, 0x0000, 0x0000 }, + { 0x1F8D, 0x1F05, 0x03B9, 0x0000 }, + { 0x24B6, 0x24D0, 0x0000, 0x0000 }, + { 0x2CBE, 0x2CBF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_147[] = { + { 0x0390, 0x03B9, 0x0308, 0x0301 }, + { 0x1F8C, 0x1F04, 0x03B9, 0x0000 }, + { 0x24B7, 0x24D1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_148[] = { + { 0x0397, 0x03B7, 0x0000, 0x0000 }, + { 0x0490, 0x0491, 0x0000, 0x0000 }, + { 0x1E8A, 0x1E8B, 0x0000, 0x0000 }, + { 0x1F8B, 0x1F03, 0x03B9, 0x0000 }, + { 0x2CB8, 0x2CB9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_149[] = { + { 0x0194, 0x0263, 0x0000, 0x0000 }, + { 0x0396, 0x03B6, 0x0000, 0x0000 }, + { 0x1F8A, 0x1F02, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_150[] = { + { 0x0197, 0x0268, 0x0000, 0x0000 }, + { 0x0395, 0x03B5, 0x0000, 0x0000 }, + { 0x0492, 0x0493, 0x0000, 0x0000 }, + { 0x1E88, 0x1E89, 0x0000, 0x0000 }, + { 0x1F89, 0x1F01, 0x03B9, 0x0000 }, + { 0x2CBA, 0x2CBB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_151[] = { + { 0x0196, 0x0269, 0x0000, 0x0000 }, + { 0x0394, 0x03B4, 0x0000, 0x0000 }, + { 0x1F88, 0x1F00, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_152[] = { + { 0x039B, 0x03BB, 0x0000, 0x0000 }, + { 0x049C, 0x049D, 0x0000, 0x0000 }, + { 0x1E86, 0x1E87, 0x0000, 0x0000 }, + { 0x1F87, 0x1F07, 0x03B9, 0x0000 }, + { 0x24BC, 0x24D6, 0x0000, 0x0000 }, + { 0x2CB4, 0x2CB5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_153[] = { + { 0x0198, 0x0199, 0x0000, 0x0000 }, + { 0x039A, 0x03BA, 0x0000, 0x0000 }, + { 0x1F86, 0x1F06, 0x03B9, 0x0000 }, + { 0x24BD, 0x24D7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_154[] = { + { 0x0399, 0x03B9, 0x0000, 0x0000 }, + { 0x049E, 0x049F, 0x0000, 0x0000 }, + { 0x1E84, 0x1E85, 0x0000, 0x0000 }, + { 0x1F85, 0x1F05, 0x03B9, 0x0000 }, + { 0x24BE, 0x24D8, 0x0000, 0x0000 }, + { 0x2CB6, 0x2CB7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_155[] = { + { 0x0398, 0x03B8, 0x0000, 0x0000 }, + { 0x1F84, 0x1F04, 0x03B9, 0x0000 }, + { 0x24BF, 0x24D9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_156[] = { + { 0x019D, 0x0272, 0x0000, 0x0000 }, + { 0x039F, 0x03BF, 0x0000, 0x0000 }, + { 0x0498, 0x0499, 0x0000, 0x0000 }, + { 0x1E82, 0x1E83, 0x0000, 0x0000 }, + { 0x1F83, 0x1F03, 0x03B9, 0x0000 }, + { 0x24B8, 0x24D2, 0x0000, 0x0000 }, + { 0x2CB0, 0x2CB1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_157[] = { + { 0x019C, 0x026F, 0x0000, 0x0000 }, + { 0x039E, 0x03BE, 0x0000, 0x0000 }, + { 0x1F82, 0x1F02, 0x03B9, 0x0000 }, + { 0x24B9, 0x24D3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_158[] = { + { 0x019F, 0x0275, 0x0000, 0x0000 }, + { 0x039D, 0x03BD, 0x0000, 0x0000 }, + { 0x049A, 0x049B, 0x0000, 0x0000 }, + { 0x1E80, 0x1E81, 0x0000, 0x0000 }, + { 0x1F81, 0x1F01, 0x03B9, 0x0000 }, + { 0x24BA, 0x24D4, 0x0000, 0x0000 }, + { 0x2CB2, 0x2CB3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_159[] = { + { 0x039C, 0x03BC, 0x0000, 0x0000 }, + { 0x1F80, 0x1F00, 0x03B9, 0x0000 }, + { 0x24BB, 0x24D5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_160[] = { + { 0x03A3, 0x03C3, 0x0000, 0x0000 }, + { 0x04A4, 0x04A5, 0x0000, 0x0000 }, + { 0x10B0, 0x2D10, 0x0000, 0x0000 }, + { 0x1EBE, 0x1EBF, 0x0000, 0x0000 }, + { 0x2C8C, 0x2C8D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_161[] = { + { 0x01A0, 0x01A1, 0x0000, 0x0000 }, + { 0x10B1, 0x2D11, 0x0000, 0x0000 }, + { 0x1FBE, 0x03B9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_162[] = { + { 0x03A1, 0x03C1, 0x0000, 0x0000 }, + { 0x04A6, 0x04A7, 0x0000, 0x0000 }, + { 0x10B2, 0x2D12, 0x0000, 0x0000 }, + { 0x1EBC, 0x1EBD, 0x0000, 0x0000 }, + { 0x2C8E, 0x2C8F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_163[] = { + { 0x01A2, 0x01A3, 0x0000, 0x0000 }, + { 0x03A0, 0x03C0, 0x0000, 0x0000 }, + { 0x10B3, 0x2D13, 0x0000, 0x0000 }, + { 0x1FBC, 0x03B1, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_164[] = { + { 0x03A7, 0x03C7, 0x0000, 0x0000 }, + { 0x04A0, 0x04A1, 0x0000, 0x0000 }, + { 0x10B4, 0x2D14, 0x0000, 0x0000 }, + { 0x1EBA, 0x1EBB, 0x0000, 0x0000 }, + { 0x1FBB, 0x1F71, 0x0000, 0x0000 }, + { 0x2C88, 0x2C89, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_165[] = { + { 0x01A4, 0x01A5, 0x0000, 0x0000 }, + { 0x03A6, 0x03C6, 0x0000, 0x0000 }, + { 0x10B5, 0x2D15, 0x0000, 0x0000 }, + { 0x1FBA, 0x1F70, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_166[] = { + { 0x01A7, 0x01A8, 0x0000, 0x0000 }, + { 0x03A5, 0x03C5, 0x0000, 0x0000 }, + { 0x04A2, 0x04A3, 0x0000, 0x0000 }, + { 0x10B6, 0x2D16, 0x0000, 0x0000 }, + { 0x1EB8, 0x1EB9, 0x0000, 0x0000 }, + { 0x1FB9, 0x1FB1, 0x0000, 0x0000 }, + { 0x2C8A, 0x2C8B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_167[] = { + { 0x01A6, 0x0280, 0x0000, 0x0000 }, + { 0x03A4, 0x03C4, 0x0000, 0x0000 }, + { 0x10B7, 0x2D17, 0x0000, 0x0000 }, + { 0x1FB8, 0x1FB0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_168[] = { + { 0x01A9, 0x0283, 0x0000, 0x0000 }, + { 0x03AB, 0x03CB, 0x0000, 0x0000 }, + { 0x04AC, 0x04AD, 0x0000, 0x0000 }, + { 0x10B8, 0x2D18, 0x0000, 0x0000 }, + { 0x1EB6, 0x1EB7, 0x0000, 0x0000 }, + { 0x1FB7, 0x03B1, 0x0342, 0x03B9 }, + { 0x2C84, 0x2C85, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_169[] = { + { 0x03AA, 0x03CA, 0x0000, 0x0000 }, + { 0x10B9, 0x2D19, 0x0000, 0x0000 }, + { 0x1FB6, 0x03B1, 0x0342, 0x0000 } +}; + +static const CaseFoldMapping case_fold_170[] = { + { 0x03A9, 0x03C9, 0x0000, 0x0000 }, + { 0x04AE, 0x04AF, 0x0000, 0x0000 }, + { 0x10BA, 0x2D1A, 0x0000, 0x0000 }, + { 0x1EB4, 0x1EB5, 0x0000, 0x0000 }, + { 0x2C86, 0x2C87, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_171[] = { + { 0x03A8, 0x03C8, 0x0000, 0x0000 }, + { 0x10BB, 0x2D1B, 0x0000, 0x0000 }, + { 0x1FB4, 0x03AC, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_172[] = { + { 0x04A8, 0x04A9, 0x0000, 0x0000 }, + { 0x10BC, 0x2D1C, 0x0000, 0x0000 }, + { 0x1EB2, 0x1EB3, 0x0000, 0x0000 }, + { 0x1FB3, 0x03B1, 0x03B9, 0x0000 }, + { 0x2C80, 0x2C81, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_173[] = { + { 0x01AC, 0x01AD, 0x0000, 0x0000 }, + { 0x10BD, 0x2D1D, 0x0000, 0x0000 }, + { 0x1FB2, 0x1F70, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_174[] = { + { 0x01AF, 0x01B0, 0x0000, 0x0000 }, + { 0x04AA, 0x04AB, 0x0000, 0x0000 }, + { 0x10BE, 0x2D1E, 0x0000, 0x0000 }, + { 0x1EB0, 0x1EB1, 0x0000, 0x0000 }, + { 0x2C82, 0x2C83, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_175[] = { + { 0x01AE, 0x0288, 0x0000, 0x0000 }, + { 0x10BF, 0x2D1F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_176[] = { + { 0x01B1, 0x028A, 0x0000, 0x0000 }, + { 0x04B4, 0x04B5, 0x0000, 0x0000 }, + { 0x10A0, 0x2D00, 0x0000, 0x0000 }, + { 0x1EAE, 0x1EAF, 0x0000, 0x0000 }, + { 0x1FAF, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C9C, 0x2C9D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_177[] = { + { 0x10A1, 0x2D01, 0x0000, 0x0000 }, + { 0x1FAE, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_178[] = { + { 0x01B3, 0x01B4, 0x0000, 0x0000 }, + { 0x04B6, 0x04B7, 0x0000, 0x0000 }, + { 0x10A2, 0x2D02, 0x0000, 0x0000 }, + { 0x1EAC, 0x1EAD, 0x0000, 0x0000 }, + { 0x1FAD, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C9E, 0x2C9F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_179[] = { + { 0x01B2, 0x028B, 0x0000, 0x0000 }, + { 0x03B0, 0x03C5, 0x0308, 0x0301 }, + { 0x10A3, 0x2D03, 0x0000, 0x0000 }, + { 0x1FAC, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_180[] = { + { 0x01B5, 0x01B6, 0x0000, 0x0000 }, + { 0x04B0, 0x04B1, 0x0000, 0x0000 }, + { 0x10A4, 0x2D04, 0x0000, 0x0000 }, + { 0x1EAA, 0x1EAB, 0x0000, 0x0000 }, + { 0x1FAB, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C98, 0x2C99, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_181[] = { + { 0x00B5, 0x03BC, 0x0000, 0x0000 }, + { 0x10A5, 0x2D05, 0x0000, 0x0000 }, + { 0x1FAA, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_182[] = { + { 0x01B7, 0x0292, 0x0000, 0x0000 }, + { 0x04B2, 0x04B3, 0x0000, 0x0000 }, + { 0x10A6, 0x2D06, 0x0000, 0x0000 }, + { 0x1EA8, 0x1EA9, 0x0000, 0x0000 }, + { 0x1FA9, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C9A, 0x2C9B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_183[] = { + { 0x10A7, 0x2D07, 0x0000, 0x0000 }, + { 0x1FA8, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_184[] = { + { 0x04BC, 0x04BD, 0x0000, 0x0000 }, + { 0x10A8, 0x2D08, 0x0000, 0x0000 }, + { 0x1EA6, 0x1EA7, 0x0000, 0x0000 }, + { 0x1FA7, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C94, 0x2C95, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_185[] = { + { 0x01B8, 0x01B9, 0x0000, 0x0000 }, + { 0x10A9, 0x2D09, 0x0000, 0x0000 }, + { 0x1FA6, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_186[] = { + { 0x04BE, 0x04BF, 0x0000, 0x0000 }, + { 0x10AA, 0x2D0A, 0x0000, 0x0000 }, + { 0x1EA4, 0x1EA5, 0x0000, 0x0000 }, + { 0x1FA5, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C96, 0x2C97, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_187[] = { + { 0x10AB, 0x2D0B, 0x0000, 0x0000 }, + { 0x1FA4, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_188[] = { + { 0x04B8, 0x04B9, 0x0000, 0x0000 }, + { 0x10AC, 0x2D0C, 0x0000, 0x0000 }, + { 0x1EA2, 0x1EA3, 0x0000, 0x0000 }, + { 0x1FA3, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C90, 0x2C91, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_189[] = { + { 0x01BC, 0x01BD, 0x0000, 0x0000 }, + { 0x10AD, 0x2D0D, 0x0000, 0x0000 }, + { 0x1FA2, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_190[] = { + { 0x04BA, 0x04BB, 0x0000, 0x0000 }, + { 0x10AE, 0x2D0E, 0x0000, 0x0000 }, + { 0x1EA0, 0x1EA1, 0x0000, 0x0000 }, + { 0x1FA1, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C92, 0x2C93, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_191[] = { + { 0x10AF, 0x2D0F, 0x0000, 0x0000 }, + { 0x1FA0, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_192[] = { + { 0x00C0, 0x00E0, 0x0000, 0x0000 }, + { 0x1EDE, 0x1EDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_193[] = { + { 0x00C1, 0x00E1, 0x0000, 0x0000 }, + { 0x03C2, 0x03C3, 0x0000, 0x0000 }, + { 0x04C5, 0x04C6, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_194[] = { + { 0x00C2, 0x00E2, 0x0000, 0x0000 }, + { 0x1EDC, 0x1EDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_195[] = { + { 0x00C3, 0x00E3, 0x0000, 0x0000 }, + { 0x04C7, 0x04C8, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_196[] = { + { 0x00C4, 0x00E4, 0x0000, 0x0000 }, + { 0x01C5, 0x01C6, 0x0000, 0x0000 }, + { 0x1EDA, 0x1EDB, 0x0000, 0x0000 }, + { 0x1FDB, 0x1F77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_197[] = { + { 0x00C5, 0x00E5, 0x0000, 0x0000 }, + { 0x01C4, 0x01C6, 0x0000, 0x0000 }, + { 0x04C1, 0x04C2, 0x0000, 0x0000 }, + { 0x1FDA, 0x1F76, 0x0000, 0x0000 }, + { 0xFF3A, 0xFF5A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_198[] = { + { 0x00C6, 0x00E6, 0x0000, 0x0000 }, + { 0x01C7, 0x01C9, 0x0000, 0x0000 }, + { 0x1ED8, 0x1ED9, 0x0000, 0x0000 }, + { 0x1FD9, 0x1FD1, 0x0000, 0x0000 }, + { 0xFF39, 0xFF59, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_199[] = { + { 0x00C7, 0x00E7, 0x0000, 0x0000 }, + { 0x04C3, 0x04C4, 0x0000, 0x0000 }, + { 0x1FD8, 0x1FD0, 0x0000, 0x0000 }, + { 0xFF38, 0xFF58, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_200[] = { + { 0x00C8, 0x00E8, 0x0000, 0x0000 }, + { 0x1ED6, 0x1ED7, 0x0000, 0x0000 }, + { 0x1FD7, 0x03B9, 0x0308, 0x0342 }, + { 0xFF37, 0xFF57, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_201[] = { + { 0x00C9, 0x00E9, 0x0000, 0x0000 }, + { 0x01C8, 0x01C9, 0x0000, 0x0000 }, + { 0x04CD, 0x04CE, 0x0000, 0x0000 }, + { 0x1FD6, 0x03B9, 0x0342, 0x0000 }, + { 0xFF36, 0xFF56, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_202[] = { + { 0x00CA, 0x00EA, 0x0000, 0x0000 }, + { 0x01CB, 0x01CC, 0x0000, 0x0000 }, + { 0x1ED4, 0x1ED5, 0x0000, 0x0000 }, + { 0xFF35, 0xFF55, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_203[] = { + { 0x00CB, 0x00EB, 0x0000, 0x0000 }, + { 0x01CA, 0x01CC, 0x0000, 0x0000 }, + { 0xFF34, 0xFF54, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_204[] = { + { 0x00CC, 0x00EC, 0x0000, 0x0000 }, + { 0x01CD, 0x01CE, 0x0000, 0x0000 }, + { 0x1ED2, 0x1ED3, 0x0000, 0x0000 }, + { 0x1FD3, 0x03B9, 0x0308, 0x0301 }, + { 0x2CE0, 0x2CE1, 0x0000, 0x0000 }, + { 0xFF33, 0xFF53, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_205[] = { + { 0x00CD, 0x00ED, 0x0000, 0x0000 }, + { 0x04C9, 0x04CA, 0x0000, 0x0000 }, + { 0x1FD2, 0x03B9, 0x0308, 0x0300 }, + { 0xFF32, 0xFF52, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_206[] = { + { 0x00CE, 0x00EE, 0x0000, 0x0000 }, + { 0x01CF, 0x01D0, 0x0000, 0x0000 }, + { 0x1ED0, 0x1ED1, 0x0000, 0x0000 }, + { 0x2CE2, 0x2CE3, 0x0000, 0x0000 }, + { 0xFF31, 0xFF51, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_207[] = { + { 0x00CF, 0x00EF, 0x0000, 0x0000 }, + { 0x04CB, 0x04CC, 0x0000, 0x0000 }, + { 0xFF30, 0xFF50, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_208[] = { + { 0x00D0, 0x00F0, 0x0000, 0x0000 }, + { 0x01D1, 0x01D2, 0x0000, 0x0000 }, + { 0x04D4, 0x04D5, 0x0000, 0x0000 }, + { 0x10C0, 0x2D20, 0x0000, 0x0000 }, + { 0x1ECE, 0x1ECF, 0x0000, 0x0000 }, + { 0xFF2F, 0xFF4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_209[] = { + { 0x00D1, 0x00F1, 0x0000, 0x0000 }, + { 0x10C1, 0x2D21, 0x0000, 0x0000 }, + { 0xFF2E, 0xFF4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_210[] = { + { 0x00D2, 0x00F2, 0x0000, 0x0000 }, + { 0x01D3, 0x01D4, 0x0000, 0x0000 }, + { 0x03D1, 0x03B8, 0x0000, 0x0000 }, + { 0x04D6, 0x04D7, 0x0000, 0x0000 }, + { 0x10C2, 0x2D22, 0x0000, 0x0000 }, + { 0x1ECC, 0x1ECD, 0x0000, 0x0000 }, + { 0xFF2D, 0xFF4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_211[] = { + { 0x00D3, 0x00F3, 0x0000, 0x0000 }, + { 0x03D0, 0x03B2, 0x0000, 0x0000 }, + { 0x10C3, 0x2D23, 0x0000, 0x0000 }, + { 0x1FCC, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF2C, 0xFF4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_212[] = { + { 0x00D4, 0x00F4, 0x0000, 0x0000 }, + { 0x01D5, 0x01D6, 0x0000, 0x0000 }, + { 0x04D0, 0x04D1, 0x0000, 0x0000 }, + { 0x10C4, 0x2D24, 0x0000, 0x0000 }, + { 0x1ECA, 0x1ECB, 0x0000, 0x0000 }, + { 0x1FCB, 0x1F75, 0x0000, 0x0000 }, + { 0xFF2B, 0xFF4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_213[] = { + { 0x00D5, 0x00F5, 0x0000, 0x0000 }, + { 0x03D6, 0x03C0, 0x0000, 0x0000 }, + { 0x10C5, 0x2D25, 0x0000, 0x0000 }, + { 0x1FCA, 0x1F74, 0x0000, 0x0000 }, + { 0xFF2A, 0xFF4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_214[] = { + { 0x00D6, 0x00F6, 0x0000, 0x0000 }, + { 0x01D7, 0x01D8, 0x0000, 0x0000 }, + { 0x03D5, 0x03C6, 0x0000, 0x0000 }, + { 0x04D2, 0x04D3, 0x0000, 0x0000 }, + { 0x1EC8, 0x1EC9, 0x0000, 0x0000 }, + { 0x1FC9, 0x1F73, 0x0000, 0x0000 }, + { 0xFF29, 0xFF49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_215[] = { + { 0x1FC8, 0x1F72, 0x0000, 0x0000 }, + { 0xFF28, 0xFF48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_216[] = { + { 0x00D8, 0x00F8, 0x0000, 0x0000 }, + { 0x01D9, 0x01DA, 0x0000, 0x0000 }, + { 0x04DC, 0x04DD, 0x0000, 0x0000 }, + { 0x1EC6, 0x1EC7, 0x0000, 0x0000 }, + { 0x1FC7, 0x03B7, 0x0342, 0x03B9 }, + { 0xFF27, 0xFF47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_217[] = { + { 0x00D9, 0x00F9, 0x0000, 0x0000 }, + { 0x03DA, 0x03DB, 0x0000, 0x0000 }, + { 0x1FC6, 0x03B7, 0x0342, 0x0000 }, + { 0xFF26, 0xFF46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_218[] = { + { 0x00DA, 0x00FA, 0x0000, 0x0000 }, + { 0x01DB, 0x01DC, 0x0000, 0x0000 }, + { 0x04DE, 0x04DF, 0x0000, 0x0000 }, + { 0x1EC4, 0x1EC5, 0x0000, 0x0000 }, + { 0xFF25, 0xFF45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_219[] = { + { 0x00DB, 0x00FB, 0x0000, 0x0000 }, + { 0x03D8, 0x03D9, 0x0000, 0x0000 }, + { 0x1FC4, 0x03AE, 0x03B9, 0x0000 }, + { 0xFF24, 0xFF44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_220[] = { + { 0x00DC, 0x00FC, 0x0000, 0x0000 }, + { 0x04D8, 0x04D9, 0x0000, 0x0000 }, + { 0x1EC2, 0x1EC3, 0x0000, 0x0000 }, + { 0x1FC3, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF23, 0xFF43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_221[] = { + { 0x00DD, 0x00FD, 0x0000, 0x0000 }, + { 0x03DE, 0x03DF, 0x0000, 0x0000 }, + { 0x1FC2, 0x1F74, 0x03B9, 0x0000 }, + { 0xFF22, 0xFF42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_222[] = { + { 0x00DE, 0x00FE, 0x0000, 0x0000 }, + { 0x04DA, 0x04DB, 0x0000, 0x0000 }, + { 0x1EC0, 0x1EC1, 0x0000, 0x0000 }, + { 0xFF21, 0xFF41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_223[] = { + { 0x00DF, 0x0073, 0x0073, 0x0000 }, + { 0x01DE, 0x01DF, 0x0000, 0x0000 }, + { 0x03DC, 0x03DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_224[] = { + { 0x04E4, 0x04E5, 0x0000, 0x0000 }, + { 0x24C4, 0x24DE, 0x0000, 0x0000 }, + { 0x2CCC, 0x2CCD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_225[] = { + { 0x01E0, 0x01E1, 0x0000, 0x0000 }, + { 0x03E2, 0x03E3, 0x0000, 0x0000 }, + { 0x24C5, 0x24DF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_226[] = { + { 0x04E6, 0x04E7, 0x0000, 0x0000 }, + { 0x24C6, 0x24E0, 0x0000, 0x0000 }, + { 0x2CCE, 0x2CCF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_227[] = { + { 0x01E2, 0x01E3, 0x0000, 0x0000 }, + { 0x03E0, 0x03E1, 0x0000, 0x0000 }, + { 0x1FFC, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C7, 0x24E1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_228[] = { + { 0x04E0, 0x04E1, 0x0000, 0x0000 }, + { 0x1FFB, 0x1F7D, 0x0000, 0x0000 }, + { 0x24C0, 0x24DA, 0x0000, 0x0000 }, + { 0x2CC8, 0x2CC9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_229[] = { + { 0x01E4, 0x01E5, 0x0000, 0x0000 }, + { 0x03E6, 0x03E7, 0x0000, 0x0000 }, + { 0x1FFA, 0x1F7C, 0x0000, 0x0000 }, + { 0x24C1, 0x24DB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_230[] = { + { 0x04E2, 0x04E3, 0x0000, 0x0000 }, + { 0x1EF8, 0x1EF9, 0x0000, 0x0000 }, + { 0x1FF9, 0x1F79, 0x0000, 0x0000 }, + { 0x24C2, 0x24DC, 0x0000, 0x0000 }, + { 0x2CCA, 0x2CCB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_231[] = { + { 0x01E6, 0x01E7, 0x0000, 0x0000 }, + { 0x03E4, 0x03E5, 0x0000, 0x0000 }, + { 0x1FF8, 0x1F78, 0x0000, 0x0000 }, + { 0x24C3, 0x24DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_232[] = { + { 0x04EC, 0x04ED, 0x0000, 0x0000 }, + { 0x1EF6, 0x1EF7, 0x0000, 0x0000 }, + { 0x1FF7, 0x03C9, 0x0342, 0x03B9 }, + { 0x24CC, 0x24E6, 0x0000, 0x0000 }, + { 0x2CC4, 0x2CC5, 0x0000, 0x0000 }, + { 0xFB13, 0x0574, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_233[] = { + { 0x01E8, 0x01E9, 0x0000, 0x0000 }, + { 0x03EA, 0x03EB, 0x0000, 0x0000 }, + { 0x1FF6, 0x03C9, 0x0342, 0x0000 }, + { 0x24CD, 0x24E7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_234[] = { + { 0x04EE, 0x04EF, 0x0000, 0x0000 }, + { 0x1EF4, 0x1EF5, 0x0000, 0x0000 }, + { 0x24CE, 0x24E8, 0x0000, 0x0000 }, + { 0x2CC6, 0x2CC7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_235[] = { + { 0x01EA, 0x01EB, 0x0000, 0x0000 }, + { 0x03E8, 0x03E9, 0x0000, 0x0000 }, + { 0x1FF4, 0x03CE, 0x03B9, 0x0000 }, + { 0x24CF, 0x24E9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_236[] = { + { 0x04E8, 0x04E9, 0x0000, 0x0000 }, + { 0x1EF2, 0x1EF3, 0x0000, 0x0000 }, + { 0x1FF3, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C8, 0x24E2, 0x0000, 0x0000 }, + { 0x2CC0, 0x2CC1, 0x0000, 0x0000 }, + { 0xFB17, 0x0574, 0x056D, 0x0000 } +}; + +static const CaseFoldMapping case_fold_237[] = { + { 0x01EC, 0x01ED, 0x0000, 0x0000 }, + { 0x03EE, 0x03EF, 0x0000, 0x0000 }, + { 0x1FF2, 0x1F7C, 0x03B9, 0x0000 }, + { 0x24C9, 0x24E3, 0x0000, 0x0000 }, + { 0xFB16, 0x057E, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_238[] = { + { 0x04EA, 0x04EB, 0x0000, 0x0000 }, + { 0x1EF0, 0x1EF1, 0x0000, 0x0000 }, + { 0x24CA, 0x24E4, 0x0000, 0x0000 }, + { 0x2CC2, 0x2CC3, 0x0000, 0x0000 }, + { 0xFB15, 0x0574, 0x056B, 0x0000 } +}; + +static const CaseFoldMapping case_fold_239[] = { + { 0x01EE, 0x01EF, 0x0000, 0x0000 }, + { 0x03EC, 0x03ED, 0x0000, 0x0000 }, + { 0x24CB, 0x24E5, 0x0000, 0x0000 }, + { 0xFB14, 0x0574, 0x0565, 0x0000 } +}; + +static const CaseFoldMapping case_fold_240[] = { + { 0x01F1, 0x01F3, 0x0000, 0x0000 }, + { 0x04F4, 0x04F5, 0x0000, 0x0000 }, + { 0x1EEE, 0x1EEF, 0x0000, 0x0000 }, + { 0x2CDC, 0x2CDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_241[] = { + { 0x01F0, 0x006A, 0x030C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_242[] = { + { 0x03F1, 0x03C1, 0x0000, 0x0000 }, + { 0x04F6, 0x04F7, 0x0000, 0x0000 }, + { 0x1EEC, 0x1EED, 0x0000, 0x0000 }, + { 0x2CDE, 0x2CDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_243[] = { + { 0x01F2, 0x01F3, 0x0000, 0x0000 }, + { 0x03F0, 0x03BA, 0x0000, 0x0000 }, + { 0x1FEC, 0x1FE5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_244[] = { + { 0x03F7, 0x03F8, 0x0000, 0x0000 }, + { 0x04F0, 0x04F1, 0x0000, 0x0000 }, + { 0x1EEA, 0x1EEB, 0x0000, 0x0000 }, + { 0x1FEB, 0x1F7B, 0x0000, 0x0000 }, + { 0x2CD8, 0x2CD9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_245[] = { + { 0x01F4, 0x01F5, 0x0000, 0x0000 }, + { 0x1FEA, 0x1F7A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_246[] = { + { 0x01F7, 0x01BF, 0x0000, 0x0000 }, + { 0x03F5, 0x03B5, 0x0000, 0x0000 }, + { 0x04F2, 0x04F3, 0x0000, 0x0000 }, + { 0x1EE8, 0x1EE9, 0x0000, 0x0000 }, + { 0x1FE9, 0x1FE1, 0x0000, 0x0000 }, + { 0x2CDA, 0x2CDB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_247[] = { + { 0x01F6, 0x0195, 0x0000, 0x0000 }, + { 0x03F4, 0x03B8, 0x0000, 0x0000 }, + { 0x1FE8, 0x1FE0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_248[] = { + { 0x1EE6, 0x1EE7, 0x0000, 0x0000 }, + { 0x1FE7, 0x03C5, 0x0308, 0x0342 }, + { 0x2CD4, 0x2CD5, 0x0000, 0x0000 }, + { 0xFB03, 0x0066, 0x0066, 0x0069 } +}; + +static const CaseFoldMapping case_fold_249[] = { + { 0x01F8, 0x01F9, 0x0000, 0x0000 }, + { 0x03FA, 0x03FB, 0x0000, 0x0000 }, + { 0x1FE6, 0x03C5, 0x0342, 0x0000 }, + { 0xFB02, 0x0066, 0x006C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_250[] = { + { 0x03F9, 0x03F2, 0x0000, 0x0000 }, + { 0x1EE4, 0x1EE5, 0x0000, 0x0000 }, + { 0x2CD6, 0x2CD7, 0x0000, 0x0000 }, + { 0xFB01, 0x0066, 0x0069, 0x0000 } +}; + +static const CaseFoldMapping case_fold_251[] = { + { 0x01FA, 0x01FB, 0x0000, 0x0000 }, + { 0x1FE4, 0x03C1, 0x0313, 0x0000 }, + { 0xFB00, 0x0066, 0x0066, 0x0000 } +}; + +static const CaseFoldMapping case_fold_252[] = { + { 0x04F8, 0x04F9, 0x0000, 0x0000 }, + { 0x1EE2, 0x1EE3, 0x0000, 0x0000 }, + { 0x1FE3, 0x03C5, 0x0308, 0x0301 }, + { 0x2CD0, 0x2CD1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_253[] = { + { 0x01FC, 0x01FD, 0x0000, 0x0000 }, + { 0x1FE2, 0x03C5, 0x0308, 0x0300 }, + { 0xFB06, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_254[] = { + { 0x1EE0, 0x1EE1, 0x0000, 0x0000 }, + { 0x2CD2, 0x2CD3, 0x0000, 0x0000 }, + { 0xFB05, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_255[] = { + { 0x01FE, 0x01FF, 0x0000, 0x0000 }, + { 0xFB04, 0x0066, 0x0066, 0x006C } +}; + + +static const CaseFoldHashBucket case_fold_hash[256] = { + { __PHYSFS_ARRAYLEN(case_fold_000), case_fold_000 }, + { __PHYSFS_ARRAYLEN(case_fold_001), case_fold_001 }, + { __PHYSFS_ARRAYLEN(case_fold_002), case_fold_002 }, + { __PHYSFS_ARRAYLEN(case_fold_003), case_fold_003 }, + { __PHYSFS_ARRAYLEN(case_fold_004), case_fold_004 }, + { __PHYSFS_ARRAYLEN(case_fold_005), case_fold_005 }, + { __PHYSFS_ARRAYLEN(case_fold_006), case_fold_006 }, + { __PHYSFS_ARRAYLEN(case_fold_007), case_fold_007 }, + { __PHYSFS_ARRAYLEN(case_fold_008), case_fold_008 }, + { __PHYSFS_ARRAYLEN(case_fold_009), case_fold_009 }, + { __PHYSFS_ARRAYLEN(case_fold_010), case_fold_010 }, + { __PHYSFS_ARRAYLEN(case_fold_011), case_fold_011 }, + { __PHYSFS_ARRAYLEN(case_fold_012), case_fold_012 }, + { __PHYSFS_ARRAYLEN(case_fold_013), case_fold_013 }, + { __PHYSFS_ARRAYLEN(case_fold_014), case_fold_014 }, + { __PHYSFS_ARRAYLEN(case_fold_015), case_fold_015 }, + { __PHYSFS_ARRAYLEN(case_fold_016), case_fold_016 }, + { __PHYSFS_ARRAYLEN(case_fold_017), case_fold_017 }, + { __PHYSFS_ARRAYLEN(case_fold_018), case_fold_018 }, + { __PHYSFS_ARRAYLEN(case_fold_019), case_fold_019 }, + { __PHYSFS_ARRAYLEN(case_fold_020), case_fold_020 }, + { __PHYSFS_ARRAYLEN(case_fold_021), case_fold_021 }, + { __PHYSFS_ARRAYLEN(case_fold_022), case_fold_022 }, + { __PHYSFS_ARRAYLEN(case_fold_023), case_fold_023 }, + { __PHYSFS_ARRAYLEN(case_fold_024), case_fold_024 }, + { __PHYSFS_ARRAYLEN(case_fold_025), case_fold_025 }, + { __PHYSFS_ARRAYLEN(case_fold_026), case_fold_026 }, + { __PHYSFS_ARRAYLEN(case_fold_027), case_fold_027 }, + { __PHYSFS_ARRAYLEN(case_fold_028), case_fold_028 }, + { __PHYSFS_ARRAYLEN(case_fold_029), case_fold_029 }, + { __PHYSFS_ARRAYLEN(case_fold_030), case_fold_030 }, + { __PHYSFS_ARRAYLEN(case_fold_031), case_fold_031 }, + { __PHYSFS_ARRAYLEN(case_fold_032), case_fold_032 }, + { __PHYSFS_ARRAYLEN(case_fold_033), case_fold_033 }, + { __PHYSFS_ARRAYLEN(case_fold_034), case_fold_034 }, + { __PHYSFS_ARRAYLEN(case_fold_035), case_fold_035 }, + { __PHYSFS_ARRAYLEN(case_fold_036), case_fold_036 }, + { __PHYSFS_ARRAYLEN(case_fold_037), case_fold_037 }, + { __PHYSFS_ARRAYLEN(case_fold_038), case_fold_038 }, + { __PHYSFS_ARRAYLEN(case_fold_039), case_fold_039 }, + { __PHYSFS_ARRAYLEN(case_fold_040), case_fold_040 }, + { __PHYSFS_ARRAYLEN(case_fold_041), case_fold_041 }, + { __PHYSFS_ARRAYLEN(case_fold_042), case_fold_042 }, + { __PHYSFS_ARRAYLEN(case_fold_043), case_fold_043 }, + { __PHYSFS_ARRAYLEN(case_fold_044), case_fold_044 }, + { __PHYSFS_ARRAYLEN(case_fold_045), case_fold_045 }, + { __PHYSFS_ARRAYLEN(case_fold_046), case_fold_046 }, + { __PHYSFS_ARRAYLEN(case_fold_047), case_fold_047 }, + { __PHYSFS_ARRAYLEN(case_fold_048), case_fold_048 }, + { __PHYSFS_ARRAYLEN(case_fold_049), case_fold_049 }, + { __PHYSFS_ARRAYLEN(case_fold_050), case_fold_050 }, + { __PHYSFS_ARRAYLEN(case_fold_051), case_fold_051 }, + { __PHYSFS_ARRAYLEN(case_fold_052), case_fold_052 }, + { __PHYSFS_ARRAYLEN(case_fold_053), case_fold_053 }, + { __PHYSFS_ARRAYLEN(case_fold_054), case_fold_054 }, + { __PHYSFS_ARRAYLEN(case_fold_055), case_fold_055 }, + { __PHYSFS_ARRAYLEN(case_fold_056), case_fold_056 }, + { __PHYSFS_ARRAYLEN(case_fold_057), case_fold_057 }, + { __PHYSFS_ARRAYLEN(case_fold_058), case_fold_058 }, + { __PHYSFS_ARRAYLEN(case_fold_059), case_fold_059 }, + { __PHYSFS_ARRAYLEN(case_fold_060), case_fold_060 }, + { __PHYSFS_ARRAYLEN(case_fold_061), case_fold_061 }, + { __PHYSFS_ARRAYLEN(case_fold_062), case_fold_062 }, + { __PHYSFS_ARRAYLEN(case_fold_063), case_fold_063 }, + { __PHYSFS_ARRAYLEN(case_fold_064), case_fold_064 }, + { __PHYSFS_ARRAYLEN(case_fold_065), case_fold_065 }, + { __PHYSFS_ARRAYLEN(case_fold_066), case_fold_066 }, + { __PHYSFS_ARRAYLEN(case_fold_067), case_fold_067 }, + { __PHYSFS_ARRAYLEN(case_fold_068), case_fold_068 }, + { __PHYSFS_ARRAYLEN(case_fold_069), case_fold_069 }, + { __PHYSFS_ARRAYLEN(case_fold_070), case_fold_070 }, + { __PHYSFS_ARRAYLEN(case_fold_071), case_fold_071 }, + { __PHYSFS_ARRAYLEN(case_fold_072), case_fold_072 }, + { __PHYSFS_ARRAYLEN(case_fold_073), case_fold_073 }, + { __PHYSFS_ARRAYLEN(case_fold_074), case_fold_074 }, + { __PHYSFS_ARRAYLEN(case_fold_075), case_fold_075 }, + { __PHYSFS_ARRAYLEN(case_fold_076), case_fold_076 }, + { __PHYSFS_ARRAYLEN(case_fold_077), case_fold_077 }, + { __PHYSFS_ARRAYLEN(case_fold_078), case_fold_078 }, + { __PHYSFS_ARRAYLEN(case_fold_079), case_fold_079 }, + { __PHYSFS_ARRAYLEN(case_fold_080), case_fold_080 }, + { __PHYSFS_ARRAYLEN(case_fold_081), case_fold_081 }, + { __PHYSFS_ARRAYLEN(case_fold_082), case_fold_082 }, + { __PHYSFS_ARRAYLEN(case_fold_083), case_fold_083 }, + { __PHYSFS_ARRAYLEN(case_fold_084), case_fold_084 }, + { __PHYSFS_ARRAYLEN(case_fold_085), case_fold_085 }, + { __PHYSFS_ARRAYLEN(case_fold_086), case_fold_086 }, + { __PHYSFS_ARRAYLEN(case_fold_087), case_fold_087 }, + { __PHYSFS_ARRAYLEN(case_fold_088), case_fold_088 }, + { __PHYSFS_ARRAYLEN(case_fold_089), case_fold_089 }, + { __PHYSFS_ARRAYLEN(case_fold_090), case_fold_090 }, + { __PHYSFS_ARRAYLEN(case_fold_091), case_fold_091 }, + { __PHYSFS_ARRAYLEN(case_fold_092), case_fold_092 }, + { __PHYSFS_ARRAYLEN(case_fold_093), case_fold_093 }, + { __PHYSFS_ARRAYLEN(case_fold_094), case_fold_094 }, + { __PHYSFS_ARRAYLEN(case_fold_095), case_fold_095 }, + { __PHYSFS_ARRAYLEN(case_fold_096), case_fold_096 }, + { __PHYSFS_ARRAYLEN(case_fold_097), case_fold_097 }, + { __PHYSFS_ARRAYLEN(case_fold_098), case_fold_098 }, + { __PHYSFS_ARRAYLEN(case_fold_099), case_fold_099 }, + { __PHYSFS_ARRAYLEN(case_fold_100), case_fold_100 }, + { __PHYSFS_ARRAYLEN(case_fold_101), case_fold_101 }, + { __PHYSFS_ARRAYLEN(case_fold_102), case_fold_102 }, + { __PHYSFS_ARRAYLEN(case_fold_103), case_fold_103 }, + { __PHYSFS_ARRAYLEN(case_fold_104), case_fold_104 }, + { __PHYSFS_ARRAYLEN(case_fold_105), case_fold_105 }, + { __PHYSFS_ARRAYLEN(case_fold_106), case_fold_106 }, + { __PHYSFS_ARRAYLEN(case_fold_107), case_fold_107 }, + { __PHYSFS_ARRAYLEN(case_fold_108), case_fold_108 }, + { __PHYSFS_ARRAYLEN(case_fold_109), case_fold_109 }, + { __PHYSFS_ARRAYLEN(case_fold_110), case_fold_110 }, + { __PHYSFS_ARRAYLEN(case_fold_111), case_fold_111 }, + { __PHYSFS_ARRAYLEN(case_fold_112), case_fold_112 }, + { __PHYSFS_ARRAYLEN(case_fold_113), case_fold_113 }, + { __PHYSFS_ARRAYLEN(case_fold_114), case_fold_114 }, + { __PHYSFS_ARRAYLEN(case_fold_115), case_fold_115 }, + { __PHYSFS_ARRAYLEN(case_fold_116), case_fold_116 }, + { __PHYSFS_ARRAYLEN(case_fold_117), case_fold_117 }, + { __PHYSFS_ARRAYLEN(case_fold_118), case_fold_118 }, + { __PHYSFS_ARRAYLEN(case_fold_119), case_fold_119 }, + { __PHYSFS_ARRAYLEN(case_fold_120), case_fold_120 }, + { __PHYSFS_ARRAYLEN(case_fold_121), case_fold_121 }, + { __PHYSFS_ARRAYLEN(case_fold_122), case_fold_122 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_124), case_fold_124 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_126), case_fold_126 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_128), case_fold_128 }, + { __PHYSFS_ARRAYLEN(case_fold_129), case_fold_129 }, + { __PHYSFS_ARRAYLEN(case_fold_130), case_fold_130 }, + { __PHYSFS_ARRAYLEN(case_fold_131), case_fold_131 }, + { __PHYSFS_ARRAYLEN(case_fold_132), case_fold_132 }, + { __PHYSFS_ARRAYLEN(case_fold_133), case_fold_133 }, + { __PHYSFS_ARRAYLEN(case_fold_134), case_fold_134 }, + { __PHYSFS_ARRAYLEN(case_fold_135), case_fold_135 }, + { __PHYSFS_ARRAYLEN(case_fold_136), case_fold_136 }, + { __PHYSFS_ARRAYLEN(case_fold_137), case_fold_137 }, + { __PHYSFS_ARRAYLEN(case_fold_138), case_fold_138 }, + { __PHYSFS_ARRAYLEN(case_fold_139), case_fold_139 }, + { __PHYSFS_ARRAYLEN(case_fold_140), case_fold_140 }, + { __PHYSFS_ARRAYLEN(case_fold_141), case_fold_141 }, + { __PHYSFS_ARRAYLEN(case_fold_142), case_fold_142 }, + { __PHYSFS_ARRAYLEN(case_fold_143), case_fold_143 }, + { __PHYSFS_ARRAYLEN(case_fold_144), case_fold_144 }, + { __PHYSFS_ARRAYLEN(case_fold_145), case_fold_145 }, + { __PHYSFS_ARRAYLEN(case_fold_146), case_fold_146 }, + { __PHYSFS_ARRAYLEN(case_fold_147), case_fold_147 }, + { __PHYSFS_ARRAYLEN(case_fold_148), case_fold_148 }, + { __PHYSFS_ARRAYLEN(case_fold_149), case_fold_149 }, + { __PHYSFS_ARRAYLEN(case_fold_150), case_fold_150 }, + { __PHYSFS_ARRAYLEN(case_fold_151), case_fold_151 }, + { __PHYSFS_ARRAYLEN(case_fold_152), case_fold_152 }, + { __PHYSFS_ARRAYLEN(case_fold_153), case_fold_153 }, + { __PHYSFS_ARRAYLEN(case_fold_154), case_fold_154 }, + { __PHYSFS_ARRAYLEN(case_fold_155), case_fold_155 }, + { __PHYSFS_ARRAYLEN(case_fold_156), case_fold_156 }, + { __PHYSFS_ARRAYLEN(case_fold_157), case_fold_157 }, + { __PHYSFS_ARRAYLEN(case_fold_158), case_fold_158 }, + { __PHYSFS_ARRAYLEN(case_fold_159), case_fold_159 }, + { __PHYSFS_ARRAYLEN(case_fold_160), case_fold_160 }, + { __PHYSFS_ARRAYLEN(case_fold_161), case_fold_161 }, + { __PHYSFS_ARRAYLEN(case_fold_162), case_fold_162 }, + { __PHYSFS_ARRAYLEN(case_fold_163), case_fold_163 }, + { __PHYSFS_ARRAYLEN(case_fold_164), case_fold_164 }, + { __PHYSFS_ARRAYLEN(case_fold_165), case_fold_165 }, + { __PHYSFS_ARRAYLEN(case_fold_166), case_fold_166 }, + { __PHYSFS_ARRAYLEN(case_fold_167), case_fold_167 }, + { __PHYSFS_ARRAYLEN(case_fold_168), case_fold_168 }, + { __PHYSFS_ARRAYLEN(case_fold_169), case_fold_169 }, + { __PHYSFS_ARRAYLEN(case_fold_170), case_fold_170 }, + { __PHYSFS_ARRAYLEN(case_fold_171), case_fold_171 }, + { __PHYSFS_ARRAYLEN(case_fold_172), case_fold_172 }, + { __PHYSFS_ARRAYLEN(case_fold_173), case_fold_173 }, + { __PHYSFS_ARRAYLEN(case_fold_174), case_fold_174 }, + { __PHYSFS_ARRAYLEN(case_fold_175), case_fold_175 }, + { __PHYSFS_ARRAYLEN(case_fold_176), case_fold_176 }, + { __PHYSFS_ARRAYLEN(case_fold_177), case_fold_177 }, + { __PHYSFS_ARRAYLEN(case_fold_178), case_fold_178 }, + { __PHYSFS_ARRAYLEN(case_fold_179), case_fold_179 }, + { __PHYSFS_ARRAYLEN(case_fold_180), case_fold_180 }, + { __PHYSFS_ARRAYLEN(case_fold_181), case_fold_181 }, + { __PHYSFS_ARRAYLEN(case_fold_182), case_fold_182 }, + { __PHYSFS_ARRAYLEN(case_fold_183), case_fold_183 }, + { __PHYSFS_ARRAYLEN(case_fold_184), case_fold_184 }, + { __PHYSFS_ARRAYLEN(case_fold_185), case_fold_185 }, + { __PHYSFS_ARRAYLEN(case_fold_186), case_fold_186 }, + { __PHYSFS_ARRAYLEN(case_fold_187), case_fold_187 }, + { __PHYSFS_ARRAYLEN(case_fold_188), case_fold_188 }, + { __PHYSFS_ARRAYLEN(case_fold_189), case_fold_189 }, + { __PHYSFS_ARRAYLEN(case_fold_190), case_fold_190 }, + { __PHYSFS_ARRAYLEN(case_fold_191), case_fold_191 }, + { __PHYSFS_ARRAYLEN(case_fold_192), case_fold_192 }, + { __PHYSFS_ARRAYLEN(case_fold_193), case_fold_193 }, + { __PHYSFS_ARRAYLEN(case_fold_194), case_fold_194 }, + { __PHYSFS_ARRAYLEN(case_fold_195), case_fold_195 }, + { __PHYSFS_ARRAYLEN(case_fold_196), case_fold_196 }, + { __PHYSFS_ARRAYLEN(case_fold_197), case_fold_197 }, + { __PHYSFS_ARRAYLEN(case_fold_198), case_fold_198 }, + { __PHYSFS_ARRAYLEN(case_fold_199), case_fold_199 }, + { __PHYSFS_ARRAYLEN(case_fold_200), case_fold_200 }, + { __PHYSFS_ARRAYLEN(case_fold_201), case_fold_201 }, + { __PHYSFS_ARRAYLEN(case_fold_202), case_fold_202 }, + { __PHYSFS_ARRAYLEN(case_fold_203), case_fold_203 }, + { __PHYSFS_ARRAYLEN(case_fold_204), case_fold_204 }, + { __PHYSFS_ARRAYLEN(case_fold_205), case_fold_205 }, + { __PHYSFS_ARRAYLEN(case_fold_206), case_fold_206 }, + { __PHYSFS_ARRAYLEN(case_fold_207), case_fold_207 }, + { __PHYSFS_ARRAYLEN(case_fold_208), case_fold_208 }, + { __PHYSFS_ARRAYLEN(case_fold_209), case_fold_209 }, + { __PHYSFS_ARRAYLEN(case_fold_210), case_fold_210 }, + { __PHYSFS_ARRAYLEN(case_fold_211), case_fold_211 }, + { __PHYSFS_ARRAYLEN(case_fold_212), case_fold_212 }, + { __PHYSFS_ARRAYLEN(case_fold_213), case_fold_213 }, + { __PHYSFS_ARRAYLEN(case_fold_214), case_fold_214 }, + { __PHYSFS_ARRAYLEN(case_fold_215), case_fold_215 }, + { __PHYSFS_ARRAYLEN(case_fold_216), case_fold_216 }, + { __PHYSFS_ARRAYLEN(case_fold_217), case_fold_217 }, + { __PHYSFS_ARRAYLEN(case_fold_218), case_fold_218 }, + { __PHYSFS_ARRAYLEN(case_fold_219), case_fold_219 }, + { __PHYSFS_ARRAYLEN(case_fold_220), case_fold_220 }, + { __PHYSFS_ARRAYLEN(case_fold_221), case_fold_221 }, + { __PHYSFS_ARRAYLEN(case_fold_222), case_fold_222 }, + { __PHYSFS_ARRAYLEN(case_fold_223), case_fold_223 }, + { __PHYSFS_ARRAYLEN(case_fold_224), case_fold_224 }, + { __PHYSFS_ARRAYLEN(case_fold_225), case_fold_225 }, + { __PHYSFS_ARRAYLEN(case_fold_226), case_fold_226 }, + { __PHYSFS_ARRAYLEN(case_fold_227), case_fold_227 }, + { __PHYSFS_ARRAYLEN(case_fold_228), case_fold_228 }, + { __PHYSFS_ARRAYLEN(case_fold_229), case_fold_229 }, + { __PHYSFS_ARRAYLEN(case_fold_230), case_fold_230 }, + { __PHYSFS_ARRAYLEN(case_fold_231), case_fold_231 }, + { __PHYSFS_ARRAYLEN(case_fold_232), case_fold_232 }, + { __PHYSFS_ARRAYLEN(case_fold_233), case_fold_233 }, + { __PHYSFS_ARRAYLEN(case_fold_234), case_fold_234 }, + { __PHYSFS_ARRAYLEN(case_fold_235), case_fold_235 }, + { __PHYSFS_ARRAYLEN(case_fold_236), case_fold_236 }, + { __PHYSFS_ARRAYLEN(case_fold_237), case_fold_237 }, + { __PHYSFS_ARRAYLEN(case_fold_238), case_fold_238 }, + { __PHYSFS_ARRAYLEN(case_fold_239), case_fold_239 }, + { __PHYSFS_ARRAYLEN(case_fold_240), case_fold_240 }, + { __PHYSFS_ARRAYLEN(case_fold_241), case_fold_241 }, + { __PHYSFS_ARRAYLEN(case_fold_242), case_fold_242 }, + { __PHYSFS_ARRAYLEN(case_fold_243), case_fold_243 }, + { __PHYSFS_ARRAYLEN(case_fold_244), case_fold_244 }, + { __PHYSFS_ARRAYLEN(case_fold_245), case_fold_245 }, + { __PHYSFS_ARRAYLEN(case_fold_246), case_fold_246 }, + { __PHYSFS_ARRAYLEN(case_fold_247), case_fold_247 }, + { __PHYSFS_ARRAYLEN(case_fold_248), case_fold_248 }, + { __PHYSFS_ARRAYLEN(case_fold_249), case_fold_249 }, + { __PHYSFS_ARRAYLEN(case_fold_250), case_fold_250 }, + { __PHYSFS_ARRAYLEN(case_fold_251), case_fold_251 }, + { __PHYSFS_ARRAYLEN(case_fold_252), case_fold_252 }, + { __PHYSFS_ARRAYLEN(case_fold_253), case_fold_253 }, + { __PHYSFS_ARRAYLEN(case_fold_254), case_fold_254 }, + { __PHYSFS_ARRAYLEN(case_fold_255), case_fold_255 }, +}; + diff --git a/3rdparty/physfs/physfs_internal.h b/3rdparty/physfs/physfs_internal.h new file mode 100644 index 0000000..9e83a7e --- /dev/null +++ b/3rdparty/physfs/physfs_internal.h @@ -0,0 +1,1496 @@ +/* + * Internal function/structure declaration. Do NOT include in your + * application. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#ifndef _INCLUDE_PHYSFS_INTERNAL_H_ +#define _INCLUDE_PHYSFS_INTERNAL_H_ + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +#include "physfs.h" + +#include /* make sure NULL is defined... */ + +#ifdef HAVE_ASSERT_H +#include +#elif (!defined assert) +#define assert(x) +#endif + +/* !!! FIXME: remove this when revamping stack allocation code... */ +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#endif + +#if defined(__sun) || defined(sun) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) \ + ( ((__GNUC__ << 16) + __GNUC_MINOR__) >= (((major) << 16) + (minor)) ) +#else +#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) (0) +#endif + +/* + * Interface for small allocations. If you need a little scratch space for + * a throwaway buffer or string, use this. It will make small allocations + * on the stack if possible, and use allocator.Malloc() if they are too + * large. This helps reduce malloc pressure. + * There are some rules, though: + * NEVER return a pointer from this, as stack-allocated buffers go away + * when your function returns. + * NEVER allocate in a loop, as stack-allocated pointers will pile up. Call + * a function that uses smallAlloc from your loop, so the allocation can + * free each time. + * NEVER call smallAlloc with any complex expression (it's a macro that WILL + * have side effects...it references the argument multiple times). Use a + * variable or a literal. + * NEVER free a pointer from this with anything but smallFree. It will not + * be a valid pointer to the allocator, regardless of where the memory came + * from. + * NEVER realloc a pointer from this. + * NEVER forget to use smallFree: it may not be a pointer from the stack. + * NEVER forget to check for NULL...allocation can fail here, of course! + */ +#define __PHYSFS_SMALLALLOCTHRESHOLD 128 +void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len); + +#define __PHYSFS_smallAlloc(bytes) ( \ + __PHYSFS_initSmallAlloc((((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \ + alloca((size_t)((bytes)+1)) : NULL), (bytes)) \ +) + +void __PHYSFS_smallFree(void *ptr); + + +/* Use the allocation hooks. */ +#define malloc(x) Do not use malloc() directly. +#define realloc(x, y) Do not use realloc() directly. +#define free(x) Do not use free() directly. +/* !!! FIXME: add alloca check here. */ + +/* The LANG section. */ +/* please send questions/translations to Ryan: icculus@icculus.org. */ + +#if (!defined PHYSFS_LANG) +# define PHYSFS_LANG PHYSFS_LANG_ENGLISH +#endif + +/* All language strings are UTF-8 encoded! */ +#define PHYSFS_LANG_ENGLISH 1 /* English by Ryan C. Gordon */ +#define PHYSFS_LANG_RUSSIAN 2 /* Russian by Ed Sinjiashvili */ +#define PHYSFS_LANG_SPANISH 3 /* Spanish by Pedro J. Pérez */ +#define PHYSFS_LANG_FRENCH 4 /* French by Stéphane Peter */ +#define PHYSFS_LANG_GERMAN 5 /* German by Michael Renner */ +#define PHYSFS_LANG_PORTUGUESE_BR 6 /* pt-br by Danny Angelo Carminati Grein */ + +#if (PHYSFS_LANG == PHYSFS_LANG_ENGLISH) + #define DIR_ARCHIVE_DESCRIPTION "Non-archive, direct filesystem I/O" + #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip compatible" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" + + #define ERR_IS_INITIALIZED "Already initialized" + #define ERR_NOT_INITIALIZED "Not initialized" + #define ERR_INVALID_ARGUMENT "Invalid argument" + #define ERR_FILES_STILL_OPEN "Files still open" + #define ERR_NO_DIR_CREATE "Failed to create directories" + #define ERR_OUT_OF_MEMORY "Out of memory" + #define ERR_NOT_IN_SEARCH_PATH "No such entry in search path" + #define ERR_NOT_SUPPORTED "Operation not supported" + #define ERR_UNSUPPORTED_ARCHIVE "Archive type unsupported" + #define ERR_NOT_A_HANDLE "Not a file handle" + #define ERR_INSECURE_FNAME "Insecure filename" + #define ERR_SYMLINK_DISALLOWED "Symbolic links are disabled" + #define ERR_NO_WRITE_DIR "Write directory is not set" + #define ERR_NO_SUCH_FILE "File not found" + #define ERR_NO_SUCH_PATH "Path not found" + #define ERR_NO_SUCH_VOLUME "Volume not found" + #define ERR_PAST_EOF "Past end of file" + #define ERR_ARC_IS_READ_ONLY "Archive is read-only" + #define ERR_IO_ERROR "I/O error" + #define ERR_CANT_SET_WRITE_DIR "Can't set write directory" + #define ERR_SYMLINK_LOOP "Infinite symbolic link loop" + #define ERR_COMPRESSION "(De)compression error" + #define ERR_NOT_IMPLEMENTED "Not implemented" + #define ERR_OS_ERROR "Operating system reported error" + #define ERR_FILE_EXISTS "File already exists" + #define ERR_NOT_A_FILE "Not a file" + #define ERR_NOT_A_DIR "Not a directory" + #define ERR_NOT_AN_ARCHIVE "Not an archive" + #define ERR_CORRUPTED "Corrupted archive" + #define ERR_SEEK_OUT_OF_RANGE "Seek out of range" + #define ERR_BAD_FILENAME "Bad filename" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS made a bad system call" + #define ERR_ARGV0_IS_NULL "argv0 is NULL" + #define ERR_NEED_DICT "need dictionary" + #define ERR_DATA_ERROR "data error" + #define ERR_MEMORY_ERROR "memory error" + #define ERR_BUFFER_ERROR "buffer error" + #define ERR_VERSION_ERROR "version error" + #define ERR_UNKNOWN_ERROR "unknown error" + #define ERR_SEARCHPATH_TRUNC "Search path was truncated" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() was truncated" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() had no dir" + #define ERR_DISK_FULL "Disk is full" + #define ERR_DIRECTORY_FULL "Directory full" + #define ERR_MACOS_GENERIC "MacOS reported error (%d)" + #define ERR_OS2_GENERIC "OS/2 reported error (%d)" + #define ERR_VOL_LOCKED_HW "Volume is locked through hardware" + #define ERR_VOL_LOCKED_SW "Volume is locked through software" + #define ERR_FILE_LOCKED "File is locked" + #define ERR_FILE_OR_DIR_BUSY "File/directory is busy" + #define ERR_FILE_ALREADY_OPEN_W "File already open for writing" + #define ERR_FILE_ALREADY_OPEN_R "File already open for reading" + #define ERR_INVALID_REFNUM "Invalid reference number" + #define ERR_GETTING_FILE_POS "Error getting file position" + #define ERR_VOLUME_OFFLINE "Volume is offline" + #define ERR_PERMISSION_DENIED "Permission denied" + #define ERR_VOL_ALREADY_ONLINE "Volume already online" + #define ERR_NO_SUCH_DRIVE "No such drive" + #define ERR_NOT_MAC_DISK "Not a Macintosh disk" + #define ERR_VOL_EXTERNAL_FS "Volume belongs to an external filesystem" + #define ERR_PROBLEM_RENAME "Problem during rename" + #define ERR_BAD_MASTER_BLOCK "Bad master directory block" + #define ERR_CANT_MOVE_FORBIDDEN "Attempt to move forbidden" + #define ERR_WRONG_VOL_TYPE "Wrong volume type" + #define ERR_SERVER_VOL_LOST "Server volume has been disconnected" + #define ERR_FILE_ID_NOT_FOUND "File ID not found" + #define ERR_FILE_ID_EXISTS "File ID already exists" + #define ERR_SERVER_NO_RESPOND "Server not responding" + #define ERR_USER_AUTH_FAILED "User authentication failed" + #define ERR_PWORD_EXPIRED "Password has expired on server" + #define ERR_ACCESS_DENIED "Access denied" + #define ERR_NOT_A_DOS_DISK "Not a DOS disk" + #define ERR_SHARING_VIOLATION "Sharing violation" + #define ERR_CANNOT_MAKE "Cannot make" + #define ERR_DEV_IN_USE "Device already in use" + #define ERR_OPEN_FAILED "Open failed" + #define ERR_PIPE_BUSY "Pipe is busy" + #define ERR_SHARING_BUF_EXCEEDED "Sharing buffer exceeded" + #define ERR_TOO_MANY_HANDLES "Too many open handles" + #define ERR_SEEK_ERROR "Seek error" + #define ERR_DEL_CWD "Trying to delete current working directory" + #define ERR_WRITE_PROTECT_ERROR "Write protect error" + #define ERR_WRITE_FAULT "Write fault" + #define ERR_LOCK_VIOLATION "Lock violation" + #define ERR_GEN_FAILURE "General failure" + #define ERR_UNCERTAIN_MEDIA "Uncertain media" + #define ERR_PROT_VIOLATION "Protection violation" + #define ERR_BROKEN_PIPE "Broken pipe" + +#elif (PHYSFS_LANG == PHYSFS_LANG_GERMAN) + #define DIR_ARCHIVE_DESCRIPTION "Kein Archiv, direkte Ein/Ausgabe in das Dateisystem" + #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip kompatibel" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Bereits initialisiert" + #define ERR_NOT_INITIALIZED "Nicht initialisiert" + #define ERR_INVALID_ARGUMENT "Ungültiges Argument" + #define ERR_FILES_STILL_OPEN "Dateien noch immer geöffnet" + #define ERR_NO_DIR_CREATE "Fehler beim Erzeugen der Verzeichnisse" + #define ERR_OUT_OF_MEMORY "Kein Speicher mehr frei" + #define ERR_NOT_IN_SEARCH_PATH "Eintrag nicht im Suchpfad enthalten" + #define ERR_NOT_SUPPORTED "Befehl nicht unterstützt" + #define ERR_UNSUPPORTED_ARCHIVE "Archiv-Typ nicht unterstützt" + #define ERR_NOT_A_HANDLE "Ist kein Dateideskriptor" + #define ERR_INSECURE_FNAME "Unsicherer Dateiname" + #define ERR_SYMLINK_DISALLOWED "Symbolische Verweise deaktiviert" + #define ERR_NO_WRITE_DIR "Schreibverzeichnis ist nicht gesetzt" + #define ERR_NO_SUCH_FILE "Datei nicht gefunden" + #define ERR_NO_SUCH_PATH "Pfad nicht gefunden" + #define ERR_NO_SUCH_VOLUME "Datencontainer nicht gefunden" + #define ERR_PAST_EOF "Hinter dem Ende der Datei" + #define ERR_ARC_IS_READ_ONLY "Archiv ist schreibgeschützt" + #define ERR_IO_ERROR "Ein/Ausgabe Fehler" + #define ERR_CANT_SET_WRITE_DIR "Kann Schreibverzeichnis nicht setzen" + #define ERR_SYMLINK_LOOP "Endlosschleife durch symbolische Verweise" + #define ERR_COMPRESSION "(De)Kompressionsfehler" + #define ERR_NOT_IMPLEMENTED "Nicht implementiert" + #define ERR_OS_ERROR "Betriebssystem meldete Fehler" + #define ERR_FILE_EXISTS "Datei existiert bereits" + #define ERR_NOT_A_FILE "Ist keine Datei" + #define ERR_NOT_A_DIR "Ist kein Verzeichnis" + #define ERR_NOT_AN_ARCHIVE "Ist kein Archiv" + #define ERR_CORRUPTED "Beschädigtes Archiv" + #define ERR_SEEK_OUT_OF_RANGE "Suche war ausserhalb der Reichweite" + #define ERR_BAD_FILENAME "Unzulässiger Dateiname" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS verursachte einen ungültigen Systemaufruf" + #define ERR_ARGV0_IS_NULL "argv0 ist NULL" + #define ERR_NEED_DICT "brauche Wörterbuch" + #define ERR_DATA_ERROR "Datenfehler" + #define ERR_MEMORY_ERROR "Speicherfehler" + #define ERR_BUFFER_ERROR "Bufferfehler" + #define ERR_VERSION_ERROR "Versionskonflikt" + #define ERR_UNKNOWN_ERROR "Unbekannter Fehler" + #define ERR_SEARCHPATH_TRUNC "Suchpfad war abgeschnitten" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() war abgeschnitten" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() bekam kein Verzeichnis" + #define ERR_DISK_FULL "Laufwerk ist voll" + #define ERR_DIRECTORY_FULL "Verzeichnis ist voll" + #define ERR_MACOS_GENERIC "MacOS meldete Fehler (%d)" + #define ERR_OS2_GENERIC "OS/2 meldete Fehler (%d)" + #define ERR_VOL_LOCKED_HW "Datencontainer ist durch Hardware gesperrt" + #define ERR_VOL_LOCKED_SW "Datencontainer ist durch Software gesperrt" + #define ERR_FILE_LOCKED "Datei ist gesperrt" + #define ERR_FILE_OR_DIR_BUSY "Datei/Verzeichnis ist beschäftigt" + #define ERR_FILE_ALREADY_OPEN_W "Datei schon im Schreibmodus geöffnet" + #define ERR_FILE_ALREADY_OPEN_R "Datei schon im Lesemodus geöffnet" + #define ERR_INVALID_REFNUM "Ungültige Referenznummer" + #define ERR_GETTING_FILE_POS "Fehler beim Finden der Dateiposition" + #define ERR_VOLUME_OFFLINE "Datencontainer ist offline" + #define ERR_PERMISSION_DENIED "Zugriff verweigert" + #define ERR_VOL_ALREADY_ONLINE "Datencontainer ist bereits online" + #define ERR_NO_SUCH_DRIVE "Laufwerk nicht vorhanden" + #define ERR_NOT_MAC_DISK "Ist kein Macintosh Laufwerk" + #define ERR_VOL_EXTERNAL_FS "Datencontainer liegt auf einem externen Dateisystem" + #define ERR_PROBLEM_RENAME "Fehler beim Umbenennen" + #define ERR_BAD_MASTER_BLOCK "Beschädigter Hauptverzeichnisblock" + #define ERR_CANT_MOVE_FORBIDDEN "Verschieben nicht erlaubt" + #define ERR_WRONG_VOL_TYPE "Falscher Datencontainer-Typ" + #define ERR_SERVER_VOL_LOST "Datencontainer am Server wurde getrennt" + #define ERR_FILE_ID_NOT_FOUND "Dateikennung nicht gefunden" + #define ERR_FILE_ID_EXISTS "Dateikennung existiert bereits" + #define ERR_SERVER_NO_RESPOND "Server antwortet nicht" + #define ERR_USER_AUTH_FAILED "Benutzerauthentifizierung fehlgeschlagen" + #define ERR_PWORD_EXPIRED "Passwort am Server ist abgelaufen" + #define ERR_ACCESS_DENIED "Zugriff verweigert" + #define ERR_NOT_A_DOS_DISK "Ist kein DOS-Laufwerk" + #define ERR_SHARING_VIOLATION "Zugriffsverletzung" + #define ERR_CANNOT_MAKE "Kann nicht erzeugen" + #define ERR_DEV_IN_USE "Gerät wird bereits benutzt" + #define ERR_OPEN_FAILED "Öffnen fehlgeschlagen" + #define ERR_PIPE_BUSY "Pipeverbindung ist belegt" + #define ERR_SHARING_BUF_EXCEEDED "Zugriffsbuffer überschritten" + #define ERR_TOO_MANY_HANDLES "Zu viele offene Dateien" + #define ERR_SEEK_ERROR "Fehler beim Suchen" + #define ERR_DEL_CWD "Aktuelles Arbeitsverzeichnis darf nicht gelöscht werden" + #define ERR_WRITE_PROTECT_ERROR "Schreibschutzfehler" + #define ERR_WRITE_FAULT "Schreibfehler" + #define ERR_LOCK_VIOLATION "Sperrverletzung" + #define ERR_GEN_FAILURE "Allgemeiner Fehler" + #define ERR_UNCERTAIN_MEDIA "Unsicheres Medium" + #define ERR_PROT_VIOLATION "Schutzverletzung" + #define ERR_BROKEN_PIPE "Pipeverbindung unterbrochen" + +#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN) + #define DIR_ARCHIVE_DESCRIPTION "Ðе архив, непоÑредÑтвенный ввод/вывод файловой ÑиÑтемы" + #define GRP_ARCHIVE_DESCRIPTION "Формат группового файла Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip ÑовмеÑтимый" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Уже инициализирован" + #define ERR_NOT_INITIALIZED "Ðе инициализирован" + #define ERR_INVALID_ARGUMENT "Ðеверный аргумент" + #define ERR_FILES_STILL_OPEN "Файлы еще открыты" + #define ERR_NO_DIR_CREATE "Ðе могу Ñоздать каталоги" + #define ERR_OUT_OF_MEMORY "КончилаÑÑŒ памÑть" + #define ERR_NOT_IN_SEARCH_PATH "Ðет такого Ñлемента в пути поиÑка" + #define ERR_NOT_SUPPORTED "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ поддерживаетÑÑ" + #define ERR_UNSUPPORTED_ARCHIVE "Ðрхивы такого типа не поддерживаютÑÑ" + #define ERR_NOT_A_HANDLE "Ðе файловый деÑкриптор" + #define ERR_INSECURE_FNAME "ÐебезопаÑное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°" + #define ERR_SYMLINK_DISALLOWED "Символьные ÑÑылки отключены" + #define ERR_NO_WRITE_DIR "Каталог Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи не уÑтановлен" + #define ERR_NO_SUCH_FILE "Файл не найден" + #define ERR_NO_SUCH_PATH "Путь не найден" + #define ERR_NO_SUCH_VOLUME "Том не найден" + #define ERR_PAST_EOF "За концом файла" + #define ERR_ARC_IS_READ_ONLY "Ðрхив только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ" + #define ERR_IO_ERROR "Ошибка ввода/вывода" + #define ERR_CANT_SET_WRITE_DIR "Ðе могу уÑтановить каталог Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи" + #define ERR_SYMLINK_LOOP "БеÑконечный цикл Ñимвольной ÑÑылки" + #define ERR_COMPRESSION "Ошибка (РаÑ)паковки" + #define ERR_NOT_IMPLEMENTED "Ðе реализовано" + #define ERR_OS_ERROR "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÑиÑтема Ñообщила ошибку" + #define ERR_FILE_EXISTS "Файл уже ÑущеÑтвует" + #define ERR_NOT_A_FILE "Ðе файл" + #define ERR_NOT_A_DIR "Ðе каталог" + #define ERR_NOT_AN_ARCHIVE "Ðе архив" + #define ERR_CORRUPTED "Поврежденный архив" + #define ERR_SEEK_OUT_OF_RANGE "Позиционирование за пределы" + #define ERR_BAD_FILENAME "Ðеверное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS выполнила неверный ÑиÑтемный вызов" + #define ERR_ARGV0_IS_NULL "argv0 is NULL" + #define ERR_NEED_DICT "нужен Ñловарь" + #define ERR_DATA_ERROR "ошибка данных" + #define ERR_MEMORY_ERROR "ошибка памÑти" + #define ERR_BUFFER_ERROR "ошибка буфера" + #define ERR_VERSION_ERROR "ошибка верÑии" + #define ERR_UNKNOWN_ERROR "неизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°" + #define ERR_SEARCHPATH_TRUNC "Путь поиÑка обрезан" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() обрезан" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() не получил каталог" + #define ERR_DISK_FULL "ДиÑк полон" + #define ERR_DIRECTORY_FULL "Каталог полон" + #define ERR_MACOS_GENERIC "MacOS Ñообщила ошибку (%d)" + #define ERR_OS2_GENERIC "OS/2 Ñообщила ошибку (%d)" + #define ERR_VOL_LOCKED_HW "Том блокирован аппаратно" + #define ERR_VOL_LOCKED_SW "Том блокирован программно" + #define ERR_FILE_LOCKED "Файл заблокирован" + #define ERR_FILE_OR_DIR_BUSY "Файл/каталог занÑÑ‚" + #define ERR_FILE_ALREADY_OPEN_W "Файл уже открыт на запиÑÑŒ" + #define ERR_FILE_ALREADY_OPEN_R "Файл уже открыт на чтение" + #define ERR_INVALID_REFNUM "Ðеверное количеÑтво ÑÑылок" + #define ERR_GETTING_FILE_POS "Ошибка при получении позиции файла" + #define ERR_VOLUME_OFFLINE "Том отÑоединен" + #define ERR_PERMISSION_DENIED "Отказано в разрешении" + #define ERR_VOL_ALREADY_ONLINE "Том уже подÑоединен" + #define ERR_NO_SUCH_DRIVE "Ðет такого диÑка" + #define ERR_NOT_MAC_DISK "Ðе диÑк Macintosh" + #define ERR_VOL_EXTERNAL_FS "Том принадлежит внешней файловой ÑиÑтеме" + #define ERR_PROBLEM_RENAME "Проблема при переименовании" + #define ERR_BAD_MASTER_BLOCK "Плохой главный блок каталога" + #define ERR_CANT_MOVE_FORBIDDEN "Попытка перемеÑтить запрещена" + #define ERR_WRONG_VOL_TYPE "Ðеверный тип тома" + #define ERR_SERVER_VOL_LOST "Серверный том был отÑоединен" + #define ERR_FILE_ID_NOT_FOUND "Идентификатор файла не найден" + #define ERR_FILE_ID_EXISTS "Идентификатор файла уже ÑущеÑтвует" + #define ERR_SERVER_NO_RESPOND "Сервер не отвечает" + #define ERR_USER_AUTH_FAILED "Ð˜Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½Ðµ удалаÑÑŒ" + #define ERR_PWORD_EXPIRED "Пароль на Ñервере уÑтарел" + #define ERR_ACCESS_DENIED "Отказано в доÑтупе" + #define ERR_NOT_A_DOS_DISK "Ðе диÑк DOS" + #define ERR_SHARING_VIOLATION "Ðарушение ÑовмеÑтного доÑтупа" + #define ERR_CANNOT_MAKE "Ðе могу Ñобрать" + #define ERR_DEV_IN_USE "УÑтройÑтво уже иÑпользуетÑÑ" + #define ERR_OPEN_FAILED "Открытие не удалоÑÑŒ" + #define ERR_PIPE_BUSY "Конвейер занÑÑ‚" + #define ERR_SHARING_BUF_EXCEEDED "РазделÑемый буфер переполнен" + #define ERR_TOO_MANY_HANDLES "Слишком много открытых деÑкрипторов" + #define ERR_SEEK_ERROR "Ошибка позиционированиÑ" + #define ERR_DEL_CWD "Попытка удалить текущий рабочий каталог" + #define ERR_WRITE_PROTECT_ERROR "Ошибка защиты запиÑи" + #define ERR_WRITE_FAULT "Ошибка запиÑи" + #define ERR_LOCK_VIOLATION "Ðарушение блокировки" + #define ERR_GEN_FAILURE "Общий Ñбой" + #define ERR_UNCERTAIN_MEDIA "Ðеопределенный ноÑитель" + #define ERR_PROT_VIOLATION "Ðарушение защиты" + #define ERR_BROKEN_PIPE "Сломанный конвейер" + + +#elif (PHYSFS_LANG == PHYSFS_LANG_FRENCH) + #define DIR_ARCHIVE_DESCRIPTION "Pas d'archive, E/S directes sur système de fichiers" + #define GRP_ARCHIVE_DESCRIPTION "Format Groupfile du moteur Build" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "Compatible PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "Format WAD du moteur DOOM" + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Déjà initialisé" + #define ERR_NOT_INITIALIZED "Non initialisé" + #define ERR_INVALID_ARGUMENT "Argument invalide" + #define ERR_FILES_STILL_OPEN "Fichiers encore ouverts" + #define ERR_NO_DIR_CREATE "Echec de la création de répertoires" + #define ERR_OUT_OF_MEMORY "A court de mémoire" + #define ERR_NOT_IN_SEARCH_PATH "Aucune entrée dans le chemin de recherche" + #define ERR_NOT_SUPPORTED "Opération non supportée" + #define ERR_UNSUPPORTED_ARCHIVE "Type d'archive non supportée" + #define ERR_NOT_A_HANDLE "Pas un descripteur de fichier" + #define ERR_INSECURE_FNAME "Nom de fichier dangereux" + #define ERR_SYMLINK_DISALLOWED "Les liens symboliques sont désactivés" + #define ERR_NO_WRITE_DIR "Le répertoire d'écriture n'est pas spécifié" + #define ERR_NO_SUCH_FILE "Fichier non trouvé" + #define ERR_NO_SUCH_PATH "Chemin non trouvé" + #define ERR_NO_SUCH_VOLUME "Volume non trouvé" + #define ERR_PAST_EOF "Au-delà de la fin du fichier" + #define ERR_ARC_IS_READ_ONLY "L'archive est en lecture seule" + #define ERR_IO_ERROR "Erreur E/S" + #define ERR_CANT_SET_WRITE_DIR "Ne peut utiliser le répertoire d'écriture" + #define ERR_SYMLINK_LOOP "Boucle infinie dans les liens symboliques" + #define ERR_COMPRESSION "Erreur de (dé)compression" + #define ERR_NOT_IMPLEMENTED "Non implémenté" + #define ERR_OS_ERROR "Erreur rapportée par le système d'exploitation" + #define ERR_FILE_EXISTS "Le fichier existe déjà" + #define ERR_NOT_A_FILE "Pas un fichier" + #define ERR_NOT_A_DIR "Pas un répertoire" + #define ERR_NOT_AN_ARCHIVE "Pas une archive" + #define ERR_CORRUPTED "Archive corrompue" + #define ERR_SEEK_OUT_OF_RANGE "Pointeur de fichier hors de portée" + #define ERR_BAD_FILENAME "Mauvais nom de fichier" + #define ERR_PHYSFS_BAD_OS_CALL "(BOGUE) PhysicsFS a fait un mauvais appel système, le salaud" + #define ERR_ARGV0_IS_NULL "argv0 est NULL" + #define ERR_NEED_DICT "a besoin du dico" + #define ERR_DATA_ERROR "erreur de données" + #define ERR_MEMORY_ERROR "erreur mémoire" + #define ERR_BUFFER_ERROR "erreur tampon" + #define ERR_VERSION_ERROR "erreur de version" + #define ERR_UNKNOWN_ERROR "erreur inconnue" + #define ERR_SEARCHPATH_TRUNC "Le chemin de recherche a été tronqué" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() a été tronqué" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() n'a pas de répertoire" + #define ERR_DISK_FULL "Disque plein" + #define ERR_DIRECTORY_FULL "Répertoire plein" + #define ERR_MACOS_GENERIC "Erreur rapportée par MacOS (%d)" + #define ERR_OS2_GENERIC "Erreur rapportée par OS/2 (%d)" + #define ERR_VOL_LOCKED_HW "Le volume est verrouillé matériellement" + #define ERR_VOL_LOCKED_SW "Le volume est verrouillé par logiciel" + #define ERR_FILE_LOCKED "Fichier verrouillé" + #define ERR_FILE_OR_DIR_BUSY "Fichier/répertoire occupé" + #define ERR_FILE_ALREADY_OPEN_W "Fichier déjà ouvert en écriture" + #define ERR_FILE_ALREADY_OPEN_R "Fichier déjà ouvert en lecture" + #define ERR_INVALID_REFNUM "Numéro de référence invalide" + #define ERR_GETTING_FILE_POS "Erreur lors de l'obtention de la position du pointeur de fichier" + #define ERR_VOLUME_OFFLINE "Le volume n'est pas en ligne" + #define ERR_PERMISSION_DENIED "Permission refusée" + #define ERR_VOL_ALREADY_ONLINE "Volumé déjà en ligne" + #define ERR_NO_SUCH_DRIVE "Lecteur inexistant" + #define ERR_NOT_MAC_DISK "Pas un disque Macintosh" + #define ERR_VOL_EXTERNAL_FS "Le volume appartient à un système de fichiers externe" + #define ERR_PROBLEM_RENAME "Problème lors du renommage" + #define ERR_BAD_MASTER_BLOCK "Mauvais block maitre de répertoire" + #define ERR_CANT_MOVE_FORBIDDEN "Essai de déplacement interdit" + #define ERR_WRONG_VOL_TYPE "Mauvais type de volume" + #define ERR_SERVER_VOL_LOST "Le volume serveur a été déconnecté" + #define ERR_FILE_ID_NOT_FOUND "Identificateur de fichier non trouvé" + #define ERR_FILE_ID_EXISTS "Identificateur de fichier existe déjà" + #define ERR_SERVER_NO_RESPOND "Le serveur ne répond pas" + #define ERR_USER_AUTH_FAILED "Authentification de l'utilisateur échouée" + #define ERR_PWORD_EXPIRED "Le mot de passe a expiré sur le serveur" + #define ERR_ACCESS_DENIED "Accès refusé" + #define ERR_NOT_A_DOS_DISK "Pas un disque DOS" + #define ERR_SHARING_VIOLATION "Violation de partage" + #define ERR_CANNOT_MAKE "Ne peut faire" + #define ERR_DEV_IN_USE "Périphérique déjà en utilisation" + #define ERR_OPEN_FAILED "Ouverture échouée" + #define ERR_PIPE_BUSY "Le tube est occupé" + #define ERR_SHARING_BUF_EXCEEDED "Tampon de partage dépassé" + #define ERR_TOO_MANY_HANDLES "Trop de descripteurs ouverts" + #define ERR_SEEK_ERROR "Erreur de positionement" + #define ERR_DEL_CWD "Essai de supprimer le répertoire courant" + #define ERR_WRITE_PROTECT_ERROR "Erreur de protection en écriture" + #define ERR_WRITE_FAULT "Erreur d'écriture" + #define ERR_LOCK_VIOLATION "Violation de verrou" + #define ERR_GEN_FAILURE "Echec général" + #define ERR_UNCERTAIN_MEDIA "Média incertain" + #define ERR_PROT_VIOLATION "Violation de protection" + #define ERR_BROKEN_PIPE "Tube cassé" + +#elif (PHYSFS_LANG == PHYSFS_LANG_PORTUGUESE_BR) + #define DIR_ARCHIVE_DESCRIPTION "Não arquivo, E/S sistema de arquivos direto" + #define GRP_ARCHIVE_DESCRIPTION "Formato Groupfile do engine Build" + #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file" + #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib" + #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II" + #define ZIP_ARCHIVE_DESCRIPTION "Formato compatível PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "Formato WAD do engine DOOM" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Já inicializado" + #define ERR_NOT_INITIALIZED "Não inicializado" + #define ERR_INVALID_ARGUMENT "Argumento inválido" + #define ERR_FILES_STILL_OPEN "Arquivos ainda abertos" + #define ERR_NO_DIR_CREATE "Falha na criação de diretórios" + #define ERR_OUT_OF_MEMORY "Memória insuficiente" + #define ERR_NOT_IN_SEARCH_PATH "Entrada não encontrada no caminho de busca" + #define ERR_NOT_SUPPORTED "Operação não suportada" + #define ERR_UNSUPPORTED_ARCHIVE "Tipo de arquivo não suportado" + #define ERR_NOT_A_HANDLE "Não é um handler de arquivo" + #define ERR_INSECURE_FNAME "Nome de arquivo inseguro" + #define ERR_SYMLINK_DISALLOWED "Links simbólicos desabilitados" + #define ERR_NO_WRITE_DIR "Diretório de escrita não definido" + #define ERR_NO_SUCH_FILE "Arquivo não encontrado" + #define ERR_NO_SUCH_PATH "Caminho não encontrado" + #define ERR_NO_SUCH_VOLUME "Volume não encontrado" + #define ERR_PAST_EOF "Passou o fim do arquivo" + #define ERR_ARC_IS_READ_ONLY "Arquivo é somente de leitura" + #define ERR_IO_ERROR "Erro de E/S" + #define ERR_CANT_SET_WRITE_DIR "Não foi possível definir diretório de escrita" + #define ERR_SYMLINK_LOOP "Loop infinito de link simbólico" + #define ERR_COMPRESSION "Erro de (Des)compressão" + #define ERR_NOT_IMPLEMENTED "Não implementado" + #define ERR_OS_ERROR "Erro reportado pelo Sistema Operacional" + #define ERR_FILE_EXISTS "Arquivo já existente" + #define ERR_NOT_A_FILE "Não é um arquivo" + #define ERR_NOT_A_DIR "Não é um diretório" + #define ERR_NOT_AN_ARCHIVE "Não é um pacote" + #define ERR_CORRUPTED "Pacote corrompido" + #define ERR_SEEK_OUT_OF_RANGE "Posicionamento além do tamanho" + #define ERR_BAD_FILENAME "Nome de arquivo inválido" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS realizou uma chamada de sistema inválida" + #define ERR_ARGV0_IS_NULL "argv0 é NULL" + #define ERR_NEED_DICT "precisa de diretório" + #define ERR_DATA_ERROR "erro nos dados" + #define ERR_MEMORY_ERROR "erro de memória" + #define ERR_BUFFER_ERROR "erro de buffer" + #define ERR_VERSION_ERROR "erro na version" + #define ERR_UNKNOWN_ERROR "erro desconhecido" + #define ERR_SEARCHPATH_TRUNC "Caminho de procura quebrado" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() foi quebrado" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() nao teve diretório" + #define ERR_DISK_FULL "Disco cheio" + #define ERR_DIRECTORY_FULL "Diretório cheio" + #define ERR_MACOS_GENERIC "MacOS reportou um erro (%d)" + #define ERR_OS2_GENERIC "OS/2 reportou um erro (%d)" + #define ERR_VOL_LOCKED_HW "Volume travado por hardware" + #define ERR_VOL_LOCKED_SW "Volume travado por software" + #define ERR_FILE_LOCKED "Arquivo travado" + #define ERR_FILE_OR_DIR_BUSY "Arquivo/Diretório está em uso" + #define ERR_FILE_ALREADY_OPEN_W "Arquivo já aberto para escrita" + #define ERR_FILE_ALREADY_OPEN_R "Arquivo já aberto para leitura" + #define ERR_INVALID_REFNUM "Número de referência" + #define ERR_GETTING_FILE_POS "Erro ao tentar obter posição do arquivo" + #define ERR_VOLUME_OFFLINE "Volume está indisponível" + #define ERR_PERMISSION_DENIED "Permissão negada" + #define ERR_VOL_ALREADY_ONLINE "Volume disponível" + #define ERR_NO_SUCH_DRIVE "Drive inexistente" + #define ERR_NOT_MAC_DISK "Não é um disco Macintosh" + #define ERR_VOL_EXTERNAL_FS "Volume pertence a um sistema de arquivos externo" + #define ERR_PROBLEM_RENAME "Problema durante renomeação" + #define ERR_BAD_MASTER_BLOCK "Bloco master do diretório inválido" + #define ERR_CANT_MOVE_FORBIDDEN "Tentativa de mover proibida" + #define ERR_WRONG_VOL_TYPE "Tipo inválido de volume" + #define ERR_SERVER_VOL_LOST "Volume servidor desconectado" + #define ERR_FILE_ID_NOT_FOUND "ID de Arquivo não encontrado" + #define ERR_FILE_ID_EXISTS "ID de Arquivo já existente" + #define ERR_SERVER_NO_RESPOND "Servidor não respondendo" + #define ERR_USER_AUTH_FAILED "Autenticação de usuário falhada" + #define ERR_PWORD_EXPIRED "Password foi expirada no servidor" + #define ERR_ACCESS_DENIED "Accesso negado" + #define ERR_NOT_A_DOS_DISK "Não é um disco DOS" + #define ERR_SHARING_VIOLATION "Violação de compartilhamento" + #define ERR_CANNOT_MAKE "Não pode ser feito" + #define ERR_DEV_IN_USE "Device já em uso" + #define ERR_OPEN_FAILED "Falaha na abertura" + #define ERR_PIPE_BUSY "Fila ocupada" + #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartilhamento excedeu" + #define ERR_TOO_MANY_HANDLES "Muitos handles abertos" + #define ERR_SEEK_ERROR "Erro de posicionamento" + #define ERR_DEL_CWD "Tentando remover diretório de trabalho atual" + #define ERR_WRITE_PROTECT_ERROR "Erro de proteção de escrita" + #define ERR_WRITE_FAULT "Erro de escrita" + #define ERR_LOCK_VIOLATION "Violação de trava" + #define ERR_GEN_FAILURE "Falha geral" + #define ERR_UNCERTAIN_MEDIA "Media incerta" + #define ERR_PROT_VIOLATION "Violação de proteção" + #define ERR_BROKEN_PIPE "Fila quebrada" + +#elif (PHYSFS_LANG == PHYSFS_LANG_SPANISH) + #define DIR_ARCHIVE_DESCRIPTION "No es un archivo, E/S directa al sistema de ficheros" + #define GRP_ARCHIVE_DESCRIPTION "Formato Build engine Groupfile" + #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file" + #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib" + #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II" + #define ZIP_ARCHIVE_DESCRIPTION "Compatible con PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Ya estaba inicializado" + #define ERR_NOT_INITIALIZED "No está inicializado" + #define ERR_INVALID_ARGUMENT "Argumento inválido" + #define ERR_FILES_STILL_OPEN "Archivos aún abiertos" + #define ERR_NO_DIR_CREATE "Fallo al crear los directorios" + #define ERR_OUT_OF_MEMORY "Memoria agotada" + #define ERR_NOT_IN_SEARCH_PATH "No existe tal entrada en la ruta de búsqueda" + #define ERR_NOT_SUPPORTED "Operación no soportada" + #define ERR_UNSUPPORTED_ARCHIVE "Tipo de archivo no soportado" + #define ERR_NOT_A_HANDLE "No es un manejador de ficheo (file handle)" + #define ERR_INSECURE_FNAME "Nombre de archivo inseguro" + #define ERR_SYMLINK_DISALLOWED "Los enlaces simbólicos están desactivados" + #define ERR_NO_WRITE_DIR "No has configurado un directorio de escritura" + #define ERR_NO_SUCH_FILE "Archivo no encontrado" + #define ERR_NO_SUCH_PATH "Ruta no encontrada" + #define ERR_NO_SUCH_VOLUME "Volumen no encontrado" + #define ERR_PAST_EOF "Te pasaste del final del archivo" + #define ERR_ARC_IS_READ_ONLY "El archivo es de sólo lectura" + #define ERR_IO_ERROR "Error E/S" + #define ERR_CANT_SET_WRITE_DIR "No puedo configurar el directorio de escritura" + #define ERR_SYMLINK_LOOP "Bucle infnito de enlaces simbólicos" + #define ERR_COMPRESSION "Error de (des)compresión" + #define ERR_NOT_IMPLEMENTED "No implementado" + #define ERR_OS_ERROR "El sistema operativo ha devuelto un error" + #define ERR_FILE_EXISTS "El archivo ya existe" + #define ERR_NOT_A_FILE "No es un archivo" + #define ERR_NOT_A_DIR "No es un directorio" + #define ERR_NOT_AN_ARCHIVE "No es un archivo" + #define ERR_CORRUPTED "Archivo corrupto" + #define ERR_SEEK_OUT_OF_RANGE "Búsqueda fuera de rango" + #define ERR_BAD_FILENAME "Nombre de archivo incorrecto" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ha hecho una llamada incorrecta al sistema" + #define ERR_ARGV0_IS_NULL "argv0 es NULL" + #define ERR_NEED_DICT "necesito diccionario" + #define ERR_DATA_ERROR "error de datos" + #define ERR_MEMORY_ERROR "error de memoria" + #define ERR_BUFFER_ERROR "error de buffer" + #define ERR_VERSION_ERROR "error de versión" + #define ERR_UNKNOWN_ERROR "error desconocido" + #define ERR_SEARCHPATH_TRUNC "La ruta de búsqueda ha sido truncada" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() ha sido truncado" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() no tenia directorio" + #define ERR_DISK_FULL "El disco está lleno" + #define ERR_DIRECTORY_FULL "El directorio está lleno" + #define ERR_MACOS_GENERIC "MacOS ha devuelto un error (%d)" + #define ERR_OS2_GENERIC "OS/2 ha devuelto un error (%d)" + #define ERR_VOL_LOCKED_HW "El volumen está bloqueado por el hardware" + #define ERR_VOL_LOCKED_SW "El volumen está bloqueado por el software" + #define ERR_FILE_LOCKED "El archivo está bloqueado" + #define ERR_FILE_OR_DIR_BUSY "Fichero o directorio ocupados" + #define ERR_FILE_ALREADY_OPEN_W "Fichero ya abierto para escritura" + #define ERR_FILE_ALREADY_OPEN_R "Fichero ya abierto para lectura" + #define ERR_INVALID_REFNUM "El número de referencia no es válido" + #define ERR_GETTING_FILE_POS "Error al tomar la posición del fichero" + #define ERR_VOLUME_OFFLINE "El volumen está desconectado" + #define ERR_PERMISSION_DENIED "Permiso denegado" + #define ERR_VOL_ALREADY_ONLINE "El volumen ya estaba conectado" + #define ERR_NO_SUCH_DRIVE "No existe tal unidad" + #define ERR_NOT_MAC_DISK "No es un disco Macintosh" + #define ERR_VOL_EXTERNAL_FS "El volumen pertence a un sistema de ficheros externo" + #define ERR_PROBLEM_RENAME "Problemas al renombrar" + #define ERR_BAD_MASTER_BLOCK "Bloque maestro de directorios incorrecto" + #define ERR_CANT_MOVE_FORBIDDEN "Intento de mover forbidden" + #define ERR_WRONG_VOL_TYPE "Tipo de volumen incorrecto" + #define ERR_SERVER_VOL_LOST "El servidor de volúmenes ha sido desconectado" + #define ERR_FILE_ID_NOT_FOUND "Identificador de archivo no encontrado" + #define ERR_FILE_ID_EXISTS "El identificador de archivo ya existe" + #define ERR_SERVER_NO_RESPOND "El servidor no responde" + #define ERR_USER_AUTH_FAILED "Fallo al autentificar el usuario" + #define ERR_PWORD_EXPIRED "La Password en el servidor ha caducado" + #define ERR_ACCESS_DENIED "Acceso denegado" + #define ERR_NOT_A_DOS_DISK "No es un disco de DOS" + #define ERR_SHARING_VIOLATION "Violación al compartir" + #define ERR_CANNOT_MAKE "No puedo hacer make" + #define ERR_DEV_IN_USE "El dispositivo ya estaba en uso" + #define ERR_OPEN_FAILED "Fallo al abrir" + #define ERR_PIPE_BUSY "Tubería ocupada" + #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartición sobrepasado" + #define ERR_TOO_MANY_HANDLES "Demasiados manejadores (handles)" + #define ERR_SEEK_ERROR "Error de búsqueda" + #define ERR_DEL_CWD "Intentando borrar el directorio de trabajo actual" + #define ERR_WRITE_PROTECT_ERROR "Error de protección contra escritura" + #define ERR_WRITE_FAULT "Fallo al escribir" + #define ERR_LOCK_VIOLATION "Violación del bloqueo" + #define ERR_GEN_FAILURE "Fallo general" + #define ERR_UNCERTAIN_MEDIA "Medio incierto" + #define ERR_PROT_VIOLATION "Violación de la protección" + #define ERR_BROKEN_PIPE "Tubería rota" + +#else + #error Please define PHYSFS_LANG. +#endif + +/* end LANG section. */ + +struct __PHYSFS_DIRHANDLE__; +struct __PHYSFS_FILEFUNCTIONS__; + + +/* !!! FIXME: find something better than "dvoid" and "fvoid" ... */ +/* Opaque data for file and dir handlers... */ +typedef void dvoid; +typedef void fvoid; + + +typedef struct +{ + /* + * Basic info about this archiver... + */ + const PHYSFS_ArchiveInfo *info; + + + /* + * DIRECTORY ROUTINES: + * These functions are for dir handles. Generate a handle with the + * openArchive() method, then pass it as the "opaque" dvoid to the + * others. + * + * Symlinks should always be followed; PhysicsFS will use the + * isSymLink() method and make a judgement on whether to + * continue to call other methods based on that. + */ + + + /* + * Returns non-zero if (filename) is a valid archive that this + * driver can handle. This filename is in platform-dependent + * notation. forWriting is non-zero if this is to be used for + * the write directory, and zero if this is to be used for an + * element of the search path. + */ + int (*isArchive)(const char *filename, int forWriting); + + /* + * Open a dirhandle for dir/archive (name). + * This filename is in platform-dependent notation. + * forWriting is non-zero if this is to be used for + * the write directory, and zero if this is to be used for an + * element of the search path. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later calls. + */ + void *(*openArchive)(const char *name, int forWriting); + + /* + * List all files in (dirname). Each file is passed to (callback), + * where a copy is made if appropriate, so you should dispose of + * it properly upon return from the callback. + * You should omit symlinks if (omitSymLinks) is non-zero. + * If you have a failure, report as much as you can. + * (dirname) is in platform-independent notation. + */ + void (*enumerateFiles)(dvoid *opaque, + const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata); + + /* + * Returns non-zero if filename can be opened for reading. + * This filename is in platform-independent notation. + * You should not follow symlinks. + */ + int (*exists)(dvoid *opaque, const char *name); + + /* + * Returns non-zero if filename is really a directory. + * This filename is in platform-independent notation. + * Symlinks should be followed; if what the symlink points + * to is missing, or isn't a directory, then the retval is zero. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + int (*isDirectory)(dvoid *opaque, const char *name, int *fileExists); + + /* + * Returns non-zero if filename is really a symlink. + * This filename is in platform-independent notation. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + int (*isSymLink)(dvoid *opaque, const char *name, int *fileExists); + + /* + * Retrieve the last modification time (mtime) of a file. + * Returns -1 on failure, or the file's mtime in seconds since + * the epoch (Jan 1, 1970) on success. + * This filename is in platform-independent notation. + * + * Regardless of success or failure, please set *exists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + PHYSFS_sint64 (*getLastModTime)(dvoid *opaque, const char *fnm, int *exist); + + /* + * Open file for reading. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Fail if the file does not exist. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + fvoid *(*openRead)(dvoid *opaque, const char *fname, int *fileExists); + + /* + * Open file for writing. + * If the file does not exist, it should be created. If it exists, + * it should be truncated to zero bytes. The writing + * offset should be the start of the file. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + fvoid *(*openWrite)(dvoid *opaque, const char *filename); + + /* + * Open file for appending. + * If the file does not exist, it should be created. The writing + * offset should be the end of the file. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + fvoid *(*openAppend)(dvoid *opaque, const char *filename); + + /* + * Delete a file in the archive/directory. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*remove)(dvoid *opaque, const char *filename); + + /* + * Create a directory in the archive/directory. + * If the application is trying to make multiple dirs, PhysicsFS + * will split them up into multiple calls before passing them to + * your driver. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*mkdir)(dvoid *opaque, const char *filename); + + /* + * Close directories/archives, and free any associated memory, + * including (opaque) itself if applicable. Implementation can assume + * that it won't be called if there are still files open from + * this archive. + */ + void (*dirClose)(dvoid *opaque); + + + + /* + * FILE ROUTINES: + * These functions are for file handles generated by the open*() methods. + * They are distinguished by taking a "fvoid" instead of a "dvoid" for + * the opaque handle. + */ + + /* + * Read more from the file. + * Returns number of objects of (objSize) bytes read from file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*read)(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); + + /* + * Write more to the file. Archives don't have to implement this. + * (Set it to NULL if not implemented). + * Returns number of objects of (objSize) bytes written to file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*write)(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); + + /* + * Returns non-zero if at end of file. + */ + int (*eof)(fvoid *opaque); + + /* + * Returns byte offset from start of file. + */ + PHYSFS_sint64 (*tell)(fvoid *opaque); + + /* + * Move read/write pointer to byte offset from start of file. + * Returns non-zero on success, zero on error. + * On failure, call __PHYSFS_setError(). + */ + int (*seek)(fvoid *opaque, PHYSFS_uint64 offset); + + /* + * Return number of bytes available in the file, or -1 if you + * aren't able to determine. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*fileLength)(fvoid *opaque); + + /* + * Close the file, and free associated resources, including (opaque) + * if applicable. Returns non-zero on success, zero if can't close + * file. On failure, call __PHYSFS_setError(). + */ + int (*fileClose)(fvoid *opaque); +} PHYSFS_Archiver; + + +/* + * Call this to set the message returned by PHYSFS_getLastError(). + * Please only use the ERR_* constants above, or add new constants to the + * above group, but I want these all in one place. + * + * Calling this with a NULL argument is a safe no-op. + */ +void __PHYSFS_setError(const char *err); + + +/* + * Convert (dirName) to platform-dependent notation, then prepend (prepend) + * and append (append) to the converted string. + * + * So, on Win32, calling: + * __PHYSFS_convertToDependent("C:\", "my/files", NULL); + * ...will return the string "C:\my\files". + * + * This is a convenience function; you might want to hack something out that + * is less generic (and therefore more efficient). + * + * Be sure to free() the return value when done with it. + */ +char *__PHYSFS_convertToDependent(const char *prepend, + const char *dirName, + const char *append); + + +/* This byteorder stuff was lifted from SDL. http://www.libsdl.org/ */ +#define PHYSFS_LIL_ENDIAN 1234 +#define PHYSFS_BIG_ENDIAN 4321 + +#if defined(__i386__) || defined(__ia64__) || \ + defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64) || \ + (defined(__alpha__) || defined(__alpha)) || \ + defined(__arm__) || defined(ARM) || \ + (defined(__mips__) && defined(__MIPSEL__)) || \ + defined(__SYMBIAN32__) || \ + defined(__x86_64__) || \ + defined(__LITTLE_ENDIAN__) +#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN +#else +#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN +#endif + + +/* + * When sorting the entries in an archive, we use a modified QuickSort. + * When there are less then PHYSFS_QUICKSORT_THRESHOLD entries left to sort, + * we switch over to a BubbleSort for the remainder. Tweak to taste. + * + * You can override this setting by defining PHYSFS_QUICKSORT_THRESHOLD + * before #including "physfs_internal.h". + */ +#ifndef PHYSFS_QUICKSORT_THRESHOLD +#define PHYSFS_QUICKSORT_THRESHOLD 4 +#endif + +/* + * Sort an array (or whatever) of (max) elements. This uses a mixture of + * a QuickSort and BubbleSort internally. + * (cmpfn) is used to determine ordering, and (swapfn) does the actual + * swapping of elements in the list. + * + * See zip.c for an example. + */ +void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)); + + +/* These get used all over for lessening code clutter. */ +#define BAIL_MACRO(e, r) { __PHYSFS_setError(e); return r; } +#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; } +#define BAIL_MACRO_MUTEX(e, m, r) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; } +#define BAIL_IF_MACRO_MUTEX(c, e, m, r) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; } +#define GOTO_MACRO(e, g) { __PHYSFS_setError(e); goto g; } +#define GOTO_IF_MACRO(c, e, g) if (c) { __PHYSFS_setError(e); goto g; } +#define GOTO_MACRO_MUTEX(e, m, g) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; } +#define GOTO_IF_MACRO_MUTEX(c, e, m, g) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; } + +#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) ) + +#if (defined __GNUC__) +#define __PHYSFS_SI64(x) x##LL +#define __PHYSFS_UI64(x) x##ULL +#elif (defined _MSC_VER) +#define __PHYSFS_SI64(x) x##i64 +#define __PHYSFS_UI64(x) x##ui64 +#else +#define __PHYSFS_SI64(x) x +#define __PHYSFS_UI64(x) x +#endif + + +/* + * Check if a ui64 will fit in the platform's address space. + * The initial sizeof check will optimize this macro out entirely on + * 64-bit (and larger?!) platforms, and the other condition will + * return zero or non-zero if the variable will fit in the platform's + * size_t, suitable to pass to malloc. This is kinda messy, but effective. + */ +#define __PHYSFS_ui64FitsAddressSpace(s) ( \ + (sizeof (PHYSFS_uint64) > sizeof (size_t)) && \ + ((s) > (__PHYSFS_UI64(0xFFFFFFFFFFFFFFFF) >> (64-(sizeof(size_t)*8)))) \ +) + + +/* + * This is a strcasecmp() or stricmp() replacement that expects both strings + * to be in UTF-8 encoding. It will do "case folding" to decide if the + * Unicode codepoints in the strings match. + * + * It will report which string is "greater than" the other, but be aware that + * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but + * a random Kanji codepoint has no meaningful alphabetically relationship to + * a Greek Lambda, but being able to assign a reliable "value" makes sorting + * algorithms possible, if not entirely sane. Most cases should treat the + * return value as "equal" or "not equal". + */ +int __PHYSFS_utf8strcasecmp(const char *s1, const char *s2); + +/* + * This works like __PHYSFS_utf8strcasecmp(), but takes a character (NOT BYTE + * COUNT) argument, like strcasencmp(). + */ +int __PHYSFS_utf8strnicmp(const char *s1, const char *s2, PHYSFS_uint32 l); + +/* + * stricmp() that guarantees to only work with low ASCII. The C runtime + * stricmp() might try to apply a locale/codepage/etc, which we don't want. + */ +int __PHYSFS_stricmpASCII(const char *s1, const char *s2); + +/* + * strnicmp() that guarantees to only work with low ASCII. The C runtime + * strnicmp() might try to apply a locale/codepage/etc, which we don't want. + */ +int __PHYSFS_strnicmpASCII(const char *s1, const char *s2, PHYSFS_uint32 l); + + +/* + * The current allocator. Not valid before PHYSFS_init is called! + */ +extern PHYSFS_Allocator __PHYSFS_AllocatorHooks; + +/* convenience macro to make this less cumbersome internally... */ +#define allocator __PHYSFS_AllocatorHooks + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*------------ ----------------*/ +/*------------ You MUST implement the following functions ----------------*/ +/*------------ if porting to a new platform. ----------------*/ +/*------------ (see platform/unix.c for an example) ----------------*/ +/*------------ ----------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + + +/* + * The dir separator; "/" on unix, "\\" on win32, ":" on MacOS, etc... + * Obviously, this isn't a function, but it IS a null-terminated string. + */ +extern const char *__PHYSFS_platformDirSeparator; + + +/* + * Initialize the platform. This is called when PHYSFS_init() is called from + * the application. You can use this to (for example) determine what version + * of Windows you're running. + * + * Return zero if there was a catastrophic failure (which prevents you from + * functioning at all), and non-zero otherwise. + */ +int __PHYSFS_platformInit(void); + + +/* + * Deinitialize the platform. This is called when PHYSFS_deinit() is called + * from the application. You can use this to clean up anything you've + * allocated in your platform driver. + * + * Return zero if there was a catastrophic failure (which prevents you from + * functioning at all), and non-zero otherwise. + */ +int __PHYSFS_platformDeinit(void); + + +/* + * Open a file for reading. (filename) is in platform-dependent notation. The + * file pointer should be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32. + * + * The same file can be opened for read multiple times, and each should have + * a unique file handle; this is frequently employed to prevent race + * conditions in the archivers. + * + * Call __PHYSFS_setError() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenRead(const char *filename); + + +/* + * Open a file for writing. (filename) is in platform-dependent notation. If + * the file exists, it should be truncated to zero bytes, and if it doesn't + * exist, it should be created as a zero-byte file. The file pointer should + * be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32, + * etc. + * + * Opening a file for write multiple times has undefined results. + * + * Call __PHYSFS_setError() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenWrite(const char *filename); + + +/* + * Open a file for appending. (filename) is in platform-dependent notation. If + * the file exists, the file pointer should be place just past the end of the + * file, so that the first write will be one byte after the current end of + * the file. If the file doesn't exist, it should be created as a zero-byte + * file. The file pointer should be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32, + * etc. + * + * Opening a file for append multiple times has undefined results. + * + * Call __PHYSFS_setError() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenAppend(const char *filename); + + +/* + * Read more data from a platform-specific file handle. (opaque) should be + * cast to whatever data type your platform uses. Read a maximum of (count) + * objects of (size) 8-bit bytes to the area pointed to by (buffer). If there + * isn't enough data available, return the number of full objects read, and + * position the file pointer at the start of the first incomplete object. + * On success, return (count) and position the file pointer one byte past + * the end of the last read object. Return (-1) if there is a catastrophic + * error, and call __PHYSFS_setError() to describe the problem; the file + * pointer should not move in such a case. + */ +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count); + +/* + * Write more data to a platform-specific file handle. (opaque) should be + * cast to whatever data type your platform uses. Write a maximum of (count) + * objects of (size) 8-bit bytes from the area pointed to by (buffer). If + * there isn't enough data available, return the number of full objects + * written, and position the file pointer at the start of the first + * incomplete object. Return (-1) if there is a catastrophic error, and call + * __PHYSFS_setError() to describe the problem; the file pointer should not + * move in such a case. + */ +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count); + +/* + * Set the file pointer to a new position. (opaque) should be cast to + * whatever data type your platform uses. (pos) specifies the number + * of 8-bit bytes to seek to from the start of the file. Seeking past the + * end of the file is an error condition, and you should check for it. + * + * Not all file types can seek; this is to be expected by the caller. + * + * On error, call __PHYSFS_setError() and return zero. On success, return + * a non-zero value. + */ +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos); + + +/* + * Get the file pointer's position, in an 8-bit byte offset from the start of + * the file. (opaque) should be cast to whatever data type your platform + * uses. + * + * Not all file types can "tell"; this is to be expected by the caller. + * + * On error, call __PHYSFS_setError() and return -1. On success, return >= 0. + */ +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque); + + +/* + * Determine the current size of a file, in 8-bit bytes, from an open file. + * + * The caller expects that this information may not be available for all + * file types on all platforms. + * + * Return -1 if you can't do it, and call __PHYSFS_setError(). Otherwise, + * return the file length in 8-bit bytes. + */ +PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle); + +/* + * Determine if a file is at EOF. (opaque) should be cast to whatever data + * type your platform uses. + * + * The caller expects that there was a short read before calling this. + * + * Return non-zero if EOF, zero if it is _not_ EOF. + */ +int __PHYSFS_platformEOF(void *opaque); + +/* + * Flush any pending writes to disk. (opaque) should be cast to whatever data + * type your platform uses. Be sure to check for errors; the caller expects + * that this function can fail if there was a flushing error, etc. + * + * Return zero on failure, non-zero on success. + */ +int __PHYSFS_platformFlush(void *opaque); + +/* + * Flush and close a file. (opaque) should be cast to whatever data type + * your platform uses. Be sure to check for errors when closing; the + * caller expects that this function can fail if there was a flushing + * error, etc. + * + * You should clean up all resources associated with (opaque). + * + * Return zero on failure, non-zero on success. + */ +int __PHYSFS_platformClose(void *opaque); + +/* + * Platform implementation of PHYSFS_getCdRomDirsCallback()... + * CD directories are discovered and reported to the callback one at a time. + * Pointers passed to the callback are assumed to be invalid to the + * application after the callback returns, so you can free them or whatever. + * Callback does not assume results will be sorted in any meaningful way. + */ +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data); + +/* + * Calculate the base dir, if your platform needs special consideration. + * Just return NULL if the standard routines will suffice. (see + * calculateBaseDir() in physfs.c ...) + * Caller will free() the retval if it's not NULL. + */ +char *__PHYSFS_platformCalcBaseDir(const char *argv0); + +/* + * Get the platform-specific user name. + * Caller will free() the retval if it's not NULL. If it's NULL, the username + * will default to "default". + */ +char *__PHYSFS_platformGetUserName(void); + +/* + * Get the platform-specific user dir. + * Caller will free() the retval if it's not NULL. If it's NULL, the userdir + * will default to basedir/username. + */ +char *__PHYSFS_platformGetUserDir(void); + +/* + * Return a pointer that uniquely identifies the current thread. + * On a platform without threading, (0x1) will suffice. These numbers are + * arbitrary; the only requirement is that no two threads have the same + * pointer. + */ +void *__PHYSFS_platformGetThreadID(void); + +/* + * Return non-zero if filename (in platform-dependent notation) exists. + * Symlinks should NOT be followed; at this stage, we do not care what the + * symlink points to. Please call __PHYSFS_SetError() with the details of + * why the file does not exist, if it doesn't; you are in a better position + * to know (path not found, bogus filename, file itself is missing, etc). + */ +int __PHYSFS_platformExists(const char *fname); + +/* + * Return the last modified time (in seconds since the epoch) of a file. + * Returns -1 on failure. (fname) is in platform-dependent notation. + * Symlinks should be followed; if what the symlink points to is missing, + * then the retval is -1. + */ +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname); + +/* + * Return non-zero if filename (in platform-dependent notation) is a symlink. + */ +int __PHYSFS_platformIsSymLink(const char *fname); + + +/* + * Return non-zero if filename (in platform-dependent notation) is a symlink. + * Symlinks should be followed; if what the symlink points to is missing, + * or isn't a directory, then the retval is false. + */ +int __PHYSFS_platformIsDirectory(const char *fname); + + +/* + * Convert (dirName) to platform-dependent notation, then prepend (prepend) + * and append (append) to the converted string. + * + * So, on Win32, calling: + * __PHYSFS_platformCvtToDependent("C:\", "my/files", NULL); + * ...will return the string "C:\my\files". + * + * This can be implemented in a platform-specific manner, so you can get + * get a speed boost that the default implementation can't, since + * you can make assumptions about the size of strings, etc.. + * + * Platforms that choose not to implement this may just call + * __PHYSFS_convertToDependent() as a passthrough, which may fit the bill + * already. + * + * Be sure to free() the return value when done with it. + */ +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append); + + +/* + * Enumerate a directory of files. This follows the rules for the + * PHYSFS_Archiver->enumerateFiles() method (see above), except that the + * (dirName) that is passed to this function is converted to + * platform-DEPENDENT notation by the caller. The PHYSFS_Archiver version + * uses platform-independent notation. Note that ".", "..", and other + * metaentries should always be ignored. + */ +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata); + + +/* + * Get the current working directory. The return value should be an + * absolute path in platform-dependent notation. The caller will deallocate + * the return value with the standard C runtime free() function when it + * is done with it. + * On error, return NULL and set the error message. + */ +char *__PHYSFS_platformCurrentDir(void); + + +/* + * Get the real physical path to a file. (path) is specified in + * platform-dependent notation, as should your return value be. + * All relative paths should be removed, leaving you with an absolute + * path. Symlinks should be resolved, too, so that the returned value is + * the most direct path to a file. + * The return value will be deallocated with the standard C runtime free() + * function when the caller is done with it. + * On error, return NULL and set the error message. + */ +char *__PHYSFS_platformRealPath(const char *path); + + +/* + * Make a directory in the actual filesystem. (path) is specified in + * platform-dependent notation. On error, return zero and set the error + * message. Return non-zero on success. + */ +int __PHYSFS_platformMkDir(const char *path); + + +/* + * Remove a file or directory entry in the actual filesystem. (path) is + * specified in platform-dependent notation. Note that this deletes files + * _and_ directories, so you might need to do some determination. + * Non-empty directories should report an error and not delete themselves + * or their contents. + * + * Deleting a symlink should remove the link, not what it points to. + * + * On error, return zero and set the error message. Return non-zero on success. + */ +int __PHYSFS_platformDelete(const char *path); + + +/* + * Create a platform-specific mutex. This can be whatever datatype your + * platform uses for mutexes, but it is cast to a (void *) for abstractness. + * + * Return (NULL) if you couldn't create one. Systems without threads can + * return any arbitrary non-NULL value. + */ +void *__PHYSFS_platformCreateMutex(void); + +/* + * Destroy a platform-specific mutex, and clean up any resources associated + * with it. (mutex) is a value previously returned by + * __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded + * platforms. + */ +void __PHYSFS_platformDestroyMutex(void *mutex); + +/* + * Grab possession of a platform-specific mutex. Mutexes should be recursive; + * that is, the same thread should be able to call this function multiple + * times in a row without causing a deadlock. This function should block + * until a thread can gain possession of the mutex. + * + * Return non-zero if the mutex was grabbed, zero if there was an + * unrecoverable problem grabbing it (this should not be a matter of + * timing out! We're talking major system errors; block until the mutex + * is available otherwise.) + * + * _DO NOT_ call __PHYSFS_setError() in here! Since setError calls this + * function, you'll cause an infinite recursion. This means you can't + * use the BAIL_*MACRO* macros, either. + */ +int __PHYSFS_platformGrabMutex(void *mutex); + +/* + * Relinquish possession of the mutex when this method has been called + * once for each time that platformGrabMutex was called. Once possession has + * been released, the next thread in line to grab the mutex (if any) may + * proceed. + * + * _DO NOT_ call __PHYSFS_setError() in here! Since setError calls this + * function, you'll cause an infinite recursion. This means you can't + * use the BAIL_*MACRO* macros, either. + */ +void __PHYSFS_platformReleaseMutex(void *mutex); + +/* + * Called at the start of PHYSFS_init() to prepare the allocator, if the user + * hasn't selected their own allocator via PHYSFS_setAllocator(). + * If the platform has a custom allocator, it should fill in the fields of + * (a) with the proper function pointers and return non-zero. + * If the platform just wants to use malloc()/free()/etc, return zero + * immediately and the higher level will handle it. The Init and Deinit + * fields of (a) are optional...set them to NULL if you don't need them. + * Everything else must be implemented. All rules follow those for + * PHYSFS_setAllocator(). If Init isn't NULL, it will be called shortly + * after this function returns non-zero. + */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a); + +#ifdef __cplusplus +} +#endif + +#endif + +/* end of physfs_internal.h ... */ + diff --git a/3rdparty/physfs/physfs_platforms.h b/3rdparty/physfs/physfs_platforms.h new file mode 100644 index 0000000..07d8679 --- /dev/null +++ b/3rdparty/physfs/physfs_platforms.h @@ -0,0 +1,53 @@ +#ifndef _INCL_PHYSFS_PLATFORMS +#define _INCL_PHYSFS_PLATFORMS + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +/* + * These only define the platforms to determine which files in the platforms + * directory should be compiled. For example, technically BeOS can be called + * a "unix" system, but since it doesn't use unix.c, we don't define + * PHYSFS_PLATFORM_UNIX on that system. + */ + +#if (defined __HAIKU__) +# define PHYSFS_PLATFORM_HAIKU +# define PHYSFS_PLATFORM_BEOS +# define PHYSFS_PLATFORM_POSIX +#elif ((defined __BEOS__) || (defined __beos__)) +# define PHYSFS_PLATFORM_BEOS +# define PHYSFS_PLATFORM_POSIX +#elif (defined _WIN32_WCE) || (defined _WIN64_WCE) +# define PHYSFS_PLATFORM_POCKETPC +#elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__)) +# define PHYSFS_PLATFORM_WINDOWS +#elif (defined OS2) +# define PHYSFS_PLATFORM_OS2 +#elif ((defined __MACH__) && (defined __APPLE__)) +/* To check if iphone or not, we need to include this file */ +# include +# if ((TARGET_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE)) +# define PHYSFS_PLATFORM_UNIX +# define PHYSFS_PLATFORM_POSIX +# define PHYSFS_NO_CDROM_SUPPORT +# else +# define PHYSFS_PLATFORM_MACOSX +# define PHYSFS_PLATFORM_POSIX +# endif +#elif defined(macintosh) +# error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X. +#elif defined(ANDROID) +# define PHYSFS_PLATFORM_UNIX +# define PHYSFS_PLATFORM_POSIX +# define PHYSFS_NO_CDROM_SUPPORT +#elif defined(unix) || defined(__unix__) +# define PHYSFS_PLATFORM_UNIX +# define PHYSFS_PLATFORM_POSIX +#else +# error Unknown platform. +#endif + +#endif /* include-once blocker. */ + diff --git a/3rdparty/physfs/physfs_unicode.c b/3rdparty/physfs/physfs_unicode.c new file mode 100644 index 0000000..15b6c03 --- /dev/null +++ b/3rdparty/physfs/physfs_unicode.c @@ -0,0 +1,465 @@ +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + + +/* + * From rfc3629, the UTF-8 spec: + * http://www.ietf.org/rfc/rfc3629.txt + * + * Char. number range | UTF-8 octet sequence + * (hexadecimal) | (binary) + * --------------------+--------------------------------------------- + * 0000 0000-0000 007F | 0xxxxxxx + * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + + +/* + * This may not be the best value, but it's one that isn't represented + * in Unicode (0x10FFFF is the largest codepoint value). We return this + * value from utf8codepoint() if there's bogus bits in the + * stream. utf8codepoint() will turn this value into something + * reasonable (like a question mark), for text that wants to try to recover, + * whereas utf8valid() will use the value to determine if a string has bad + * bits. + */ +#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF + +/* + * This is the codepoint we currently return when there was bogus bits in a + * UTF-8 string. May not fly in Asian locales? + */ +#define UNICODE_BOGUS_CHAR_CODEPOINT '?' + +static PHYSFS_uint32 utf8codepoint(const char **_str) +{ + const char *str = *_str; + PHYSFS_uint32 retval = 0; + PHYSFS_uint32 octet = (PHYSFS_uint32) ((PHYSFS_uint8) *str); + PHYSFS_uint32 octet2, octet3, octet4; + + if (octet == 0) /* null terminator, end of string. */ + return 0; + + else if (octet < 128) /* one octet char: 0 to 127 */ + { + (*_str)++; /* skip to next possible start of codepoint. */ + return(octet); + } /* else if */ + + else if ((octet > 127) && (octet < 192)) /* bad (starts with 10xxxxxx). */ + { + /* + * Apparently each of these is supposed to be flagged as a bogus + * char, instead of just resyncing to the next valid codepoint. + */ + (*_str)++; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + else if (octet < 224) /* two octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet -= (128+64); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 1; /* skip to next possible start of codepoint. */ + retval = ((octet << 6) | (octet2 - 128)); + if ((retval >= 0x80) && (retval <= 0x7FF)) + return retval; + } /* else if */ + + else if (octet < 240) /* three octets */ + { + (*_str)++; // advance at least one byte in case of an error + octet -= (128+64+32); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 2; /* skip to next possible start of codepoint. */ + retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) ); + + /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */ + switch (retval) + { + case 0xD800: + case 0xDB7F: + case 0xDB80: + case 0xDBFF: + case 0xDC00: + case 0xDF80: + case 0xDFFF: + return UNICODE_BOGUS_CHAR_VALUE; + } /* switch */ + + /* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */ + if ((retval >= 0x800) && (retval <= 0xFFFD)) + return retval; + } /* else if */ + + else if (octet < 248) /* four octets */ + { + (*_str)++; // advance at least one byte in case of an error + octet -= (128+64+32+16); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet4 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet4 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 3; /* skip to next possible start of codepoint. */ + retval = ( ((octet << 18)) | ((octet2 - 128) << 12) | + ((octet3 - 128) << 6) | ((octet4 - 128)) ); + if ((retval >= 0x10000) && (retval <= 0x10FFFF)) + return retval; + } /* else if */ + + /* + * Five and six octet sequences became illegal in rfc3629. + * We throw the codepoint away, but parse them to make sure we move + * ahead the right number of bytes and don't overflow the buffer. + */ + + else if (octet < 252) /* five octets */ + { + (*_str)++; // advance at least one byte in case of an error + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 4; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + else /* six octets */ + { + (*_str)++; // advance at least one byte in case of an error + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 5; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + return UNICODE_BOGUS_CHAR_VALUE; +} /* utf8codepoint */ + + +void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len) +{ + len -= sizeof (PHYSFS_uint32); /* save room for null char. */ + while (len >= sizeof (PHYSFS_uint32)) + { + PHYSFS_uint32 cp = utf8codepoint(&src); + if (cp == 0) + break; + else if (cp == UNICODE_BOGUS_CHAR_VALUE) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + *(dst++) = cp; + len -= sizeof (PHYSFS_uint32); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUcs4 */ + + +void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) +{ + len -= sizeof (PHYSFS_uint16); /* save room for null char. */ + while (len >= sizeof (PHYSFS_uint16)) + { + PHYSFS_uint32 cp = utf8codepoint(&src); + if (cp == 0) + break; + else if (cp == UNICODE_BOGUS_CHAR_VALUE) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + /* !!! BLUESKY: UTF-16 surrogates? */ + if (cp > 0xFFFF) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + *(dst++) = cp; + len -= sizeof (PHYSFS_uint16); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUcs2 */ + +static void utf8fromcodepoint(PHYSFS_uint32 cp, char **_dst, PHYSFS_uint64 *_len) +{ + char *dst = *_dst; + PHYSFS_uint64 len = *_len; + + if (len == 0) + return; + + if (cp > 0x10FFFF) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else if ((cp == 0xFFFE) || (cp == 0xFFFF)) /* illegal values. */ + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else + { + /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */ + switch (cp) + { + case 0xD800: + case 0xDB7F: + case 0xDB80: + case 0xDBFF: + case 0xDC00: + case 0xDF80: + case 0xDFFF: + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + } /* switch */ + } /* else */ + + /* Do the encoding... */ + if (cp < 0x80) + { + *(dst++) = (char) cp; + len--; + } /* if */ + + else if (cp < 0x800) + { + if (len < 2) + len = 0; + else + { + *(dst++) = (char) ((cp >> 6) | 128 | 64); + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 2; + } /* else */ + } /* else if */ + + else if (cp < 0x10000) + { + if (len < 3) + len = 0; + else + { + *(dst++) = (char) ((cp >> 12) | 128 | 64 | 32); + *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 3; + } /* else */ + } /* else if */ + + else + { + if (len < 4) + len = 0; + else + { + *(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16); + *(dst++) = (char) ((cp >> 12) & 0x3F) | 128; + *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 4; + } /* else if */ + } /* else */ + + *_dst = dst; + *_len = len; +} /* utf8fromcodepoint */ + +#define UTF8FROMTYPE(typ, src, dst, len) \ + if (len == 0) return; \ + len--; \ + while (len) \ + { \ + const PHYSFS_uint32 cp = (PHYSFS_uint32) ((typ) (*(src++))); \ + if (cp == 0) break; \ + utf8fromcodepoint(cp, &dst, &len); \ + } \ + *dst = '\0'; \ + +void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint32, src, dst, len); +} /* PHYSFS_utf8FromUcs4 */ + +void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint64, src, dst, len); +} /* PHYSFS_utf8FromUcs4 */ + +/* latin1 maps to unicode codepoints directly, we just utf-8 encode it. */ +void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint8, src, dst, len); +} /* PHYSFS_utf8FromLatin1 */ + +#undef UTF8FROMTYPE + + +typedef struct CaseFoldMapping +{ + PHYSFS_uint32 from; + PHYSFS_uint32 to0; + PHYSFS_uint32 to1; + PHYSFS_uint32 to2; +} CaseFoldMapping; + +typedef struct CaseFoldHashBucket +{ + const PHYSFS_uint8 count; + const CaseFoldMapping *list; +} CaseFoldHashBucket; + +#include "physfs_casefolding.h" + +static void locate_case_fold_mapping(const PHYSFS_uint32 from, + PHYSFS_uint32 *to) +{ + PHYSFS_uint32 i; + const PHYSFS_uint8 hashed = ((from ^ (from >> 8)) & 0xFF); + const CaseFoldHashBucket *bucket = &case_fold_hash[hashed]; + const CaseFoldMapping *mapping = bucket->list; + + for (i = 0; i < bucket->count; i++, mapping++) + { + if (mapping->from == from) + { + to[0] = mapping->to0; + to[1] = mapping->to1; + to[2] = mapping->to2; + return; + } /* if */ + } /* for */ + + /* Not found...there's no remapping for this codepoint. */ + to[0] = from; + to[1] = 0; + to[2] = 0; +} /* locate_case_fold_mapping */ + + +static int utf8codepointcmp(const PHYSFS_uint32 cp1, const PHYSFS_uint32 cp2) +{ + PHYSFS_uint32 folded1[3], folded2[3]; + locate_case_fold_mapping(cp1, folded1); + locate_case_fold_mapping(cp2, folded2); + return ( (folded1[0] == folded2[0]) && + (folded1[1] == folded2[1]) && + (folded1[2] == folded2[2]) ); +} /* utf8codepointcmp */ + + +int __PHYSFS_utf8strcasecmp(const char *str1, const char *str2) +{ + while (1) + { + const PHYSFS_uint32 cp1 = utf8codepoint(&str1); + const PHYSFS_uint32 cp2 = utf8codepoint(&str2); + if (!utf8codepointcmp(cp1, cp2)) return 0; + if (cp1 == 0) return 1; + } /* while */ + + return 0; /* shouldn't hit this. */ +} /* __PHYSFS_utf8strcasecmp */ + + +int __PHYSFS_utf8strnicmp(const char *str1, const char *str2, PHYSFS_uint32 n) +{ + while (n > 0) + { + const PHYSFS_uint32 cp1 = utf8codepoint(&str1); + const PHYSFS_uint32 cp2 = utf8codepoint(&str2); + if (!utf8codepointcmp(cp1, cp2)) return 0; + if (cp1 == 0) return 1; + n--; + } /* while */ + + return 1; /* matched to n chars. */ +} /* __PHYSFS_utf8strnicmp */ + + +int __PHYSFS_stricmpASCII(const char *str1, const char *str2) +{ + while (1) + { + const char ch1 = *(str1++); + const char ch2 = *(str2++); + const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1; + const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2; + if (cp1 < cp2) + return -1; + else if (cp1 > cp2) + return 1; + else if (cp1 == 0) /* they're both null chars? */ + return 0; + } /* while */ + + return 0; /* shouldn't hit this. */ +} /* __PHYSFS_stricmpASCII */ + + +int __PHYSFS_strnicmpASCII(const char *str1, const char *str2, PHYSFS_uint32 n) +{ + while (n-- > 0) + { + const char ch1 = *(str1++); + const char ch2 = *(str2++); + const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1; + const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2; + if (cp1 < cp2) + return -1; + else if (cp1 > cp2) + return 1; + else if (cp1 == 0) /* they're both null chars? */ + return 0; + } /* while */ + + return 0; +} /* __PHYSFS_stricmpASCII */ + + +/* end of physfs_unicode.c ... */ + diff --git a/3rdparty/physfs/platform/beos.cpp b/3rdparty/physfs/platform/beos.cpp new file mode 100644 index 0000000..ee7b190 --- /dev/null +++ b/3rdparty/physfs/platform/beos.cpp @@ -0,0 +1,256 @@ +/* + * BeOS platform-dependent support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_BEOS + +#ifdef PHYSFS_PLATFORM_HAIKU +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + + +int __PHYSFS_platformInit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +static char *getMountPoint(const char *devname) +{ + BVolumeRoster mounts; + BVolume vol; + + mounts.Rewind(); + while (mounts.GetNextVolume(&vol) == B_NO_ERROR) + { + fs_info fsinfo; + fs_stat_dev(vol.Device(), &fsinfo); + if (strcmp(devname, fsinfo.device_name) == 0) + { + //char buf[B_FILE_NAME_LENGTH]; + BDirectory directory; + BEntry entry; + BPath path; + status_t rc; + rc = vol.GetRootDirectory(&directory); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + rc = directory.GetEntry(&entry); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + rc = entry.GetPath(&path); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + const char *str = path.Path(); + BAIL_IF_MACRO(str == NULL, ERR_OS_ERROR, NULL); /* ?! */ + char *retval = (char *) allocator.Malloc(strlen(str) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, str); + return(retval); + } /* if */ + } /* while */ + + return(NULL); +} /* getMountPoint */ + + + /* + * This function is lifted from Simple Directmedia Layer (SDL): + * http://www.libsdl.org/ + */ +static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data) +{ + BDirectory dir; + dir.SetTo(d); + if (dir.InitCheck() != B_NO_ERROR) + return; + + dir.Rewind(); + BEntry entry; + while (dir.GetNextEntry(&entry) >= 0) + { + BPath path; + const char *name; + entry_ref e; + + if (entry.GetPath(&path) != B_NO_ERROR) + continue; + + name = path.Path(); + + if (entry.GetRef(&e) != B_NO_ERROR) + continue; + + if (entry.IsDirectory()) + { + if (strcmp(e.name, "floppy") != 0) + tryDir(name, callback, data); + } /* if */ + + else + { + bool add_it = false; + int devfd; + device_geometry g; + + if (strcmp(e.name, "raw") == 0) /* ignore partitions. */ + { + int devfd = open(name, O_RDONLY); + if (devfd >= 0) + { + if (ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) + { + if (g.device_type == B_CD) + { + char *mntpnt = getMountPoint(name); + if (mntpnt != NULL) + { + callback(data, mntpnt); + allocator.Free(mntpnt); /* !!! FIXME: lose this malloc! */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + + close(devfd); + } /* else */ + } /* while */ +} /* tryDir */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + tryDir("/dev/disk", cb, data); +} /* __PHYSFS_platformDetectAvailableCDs */ + + +static team_id getTeamID(void) +{ + thread_info info; + thread_id tid = find_thread(NULL); + get_thread_info(tid, &info); + return(info.team); +} /* getTeamID */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + image_info info; + int32 cookie = 0; + + while (get_next_image_info(0, &cookie, &info) == B_OK) { + if (info.type == B_APP_IMAGE) + break; + } + + BEntry entry(info.name, true); + BPath path; + status_t rc = entry.GetPath(&path); /* (path) now has binary's path. */ + assert(rc == B_OK); + rc = path.GetParent(&path); /* chop filename, keep directory. */ + assert(rc == B_OK); + const char *str = path.Path(); + assert(str != NULL); + char *retval = (char *) allocator.Malloc(strlen(str) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, str); + return(retval); +} /* __PHYSFS_platformCalcBaseDir */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + return((void *) find_thread(NULL)); +} /* __PHYSFS_platformGetThreadID */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + BPath normalized(path, NULL, true); /* force normalization of path. */ + const char *resolved_path = normalized.Path(); + BAIL_IF_MACRO(resolved_path == NULL, ERR_NO_SUCH_FILE, NULL); + char *retval = (char *) allocator.Malloc(strlen(resolved_path) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, resolved_path); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return(__PHYSFS_platformRealPath(".")); /* let BPath sort it out. */ +} /* __PHYSFS_platformCurrentDir */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + return(new BLocker("PhysicsFS lock", true)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + delete ((BLocker *) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return ((BLocker *) mutex)->Lock() ? 1 : 0; +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ((BLocker *) mutex)->Unlock(); +} /* __PHYSFS_platformReleaseMutex */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_BEOS */ + +/* end of beos.cpp ... */ + diff --git a/3rdparty/physfs/platform/macosx.c b/3rdparty/physfs/platform/macosx.c new file mode 100644 index 0000000..248f905 --- /dev/null +++ b/3rdparty/physfs/platform/macosx.c @@ -0,0 +1,419 @@ +/* + * Mac OS X support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_MACOSX + +#include +#include +#include +#include +#include +#include + +/* Seems to get defined in some system header... */ +#ifdef Free +#undef Free +#endif + +#include "physfs_internal.h" + + +/* Wrap PHYSFS_Allocator in a CFAllocator... */ +static CFAllocatorRef cfallocator = NULL; + +CFStringRef cfallocCopyDesc(const void *info) +{ + return(CFStringCreateWithCString(cfallocator, "PhysicsFS", + kCFStringEncodingASCII)); +} /* cfallocCopyDesc */ + + +static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info) +{ + return allocator.Malloc(allocSize); +} /* cfallocMalloc */ + + +static void cfallocFree(void *ptr, void *info) +{ + allocator.Free(ptr); +} /* cfallocFree */ + + +static void *cfallocRealloc(void *ptr, CFIndex newsize, + CFOptionFlags hint, void *info) +{ + if ((ptr == NULL) || (newsize <= 0)) + return NULL; /* ADC docs say you should always return NULL here. */ + return allocator.Realloc(ptr, newsize); +} /* cfallocRealloc */ + + +int __PHYSFS_platformInit(void) +{ + /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */ + CFAllocatorContext ctx; + memset(&ctx, '\0', sizeof (ctx)); + ctx.copyDescription = cfallocCopyDesc; + ctx.allocate = cfallocMalloc; + ctx.reallocate = cfallocRealloc; + ctx.deallocate = cfallocFree; + cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx); + BAIL_IF_MACRO(cfallocator == NULL, ERR_OUT_OF_MEMORY, 0); + return(1); /* success. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + CFRelease(cfallocator); + cfallocator = NULL; + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +/* CD-ROM detection code... */ + +/* + * Code based on sample from Apple Developer Connection: + * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm + */ + +static int darwinIsWholeMedia(io_service_t service) +{ + int retval = 0; + CFTypeRef wholeMedia; + + if (!IOObjectConformsTo(service, kIOMediaClass)) + return(0); + + wholeMedia = IORegistryEntryCreateCFProperty(service, + CFSTR(kIOMediaWholeKey), + cfallocator, 0); + if (wholeMedia == NULL) + return(0); + + retval = CFBooleanGetValue(wholeMedia); + CFRelease(wholeMedia); + + return retval; +} /* darwinIsWholeMedia */ + + +static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) +{ + int retval = 0; + CFMutableDictionaryRef matchingDict; + kern_return_t rc; + io_iterator_t iter; + io_service_t service; + + if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL) + return(0); + + rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter); + if ((rc != KERN_SUCCESS) || (!iter)) + return(0); + + service = IOIteratorNext(iter); + IOObjectRelease(iter); + if (!service) + return(0); + + rc = IORegistryEntryCreateIterator(service, kIOServicePlane, + kIORegistryIterateRecursively | kIORegistryIterateParents, &iter); + + if (!iter) + return(0); + + if (rc != KERN_SUCCESS) + { + IOObjectRelease(iter); + return(0); + } /* if */ + + IOObjectRetain(service); /* add an extra object reference... */ + + do + { + if (darwinIsWholeMedia(service)) + { + if ( (IOObjectConformsTo(service, kIOCDMediaClass)) || + (IOObjectConformsTo(service, kIODVDMediaClass)) ) + { + retval = 1; + } /* if */ + } /* if */ + IOObjectRelease(service); + } while ((service = IOIteratorNext(iter)) && (!retval)); + + IOObjectRelease(iter); + IOObjectRelease(service); + + return(retval); +} /* darwinIsMountedDisc */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + const char *devPrefix = "/dev/"; + const int prefixLen = strlen(devPrefix); + mach_port_t masterPort = 0; + struct statfs *mntbufp; + int i, mounts; + + if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS) + BAIL_MACRO(ERR_OS_ERROR, ) /*return void*/; + + mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */ + for (i = 0; i < mounts; i++) + { + char *dev = mntbufp[i].f_mntfromname; + char *mnt = mntbufp[i].f_mntonname; + if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */ + continue; + + dev += prefixLen; + if (darwinIsMountedDisc(dev, masterPort)) + cb(data, mnt); + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +static char *convertCFString(CFStringRef cfstr) +{ + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), + kCFStringEncodingUTF8) + 1; + char *retval = (char *) allocator.Malloc(len); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8)) + { + /* shrink overallocated buffer if possible... */ + CFIndex newlen = strlen(retval) + 1; + if (newlen < len) + { + void *ptr = allocator.Realloc(retval, newlen); + if (ptr != NULL) + retval = (char *) ptr; + } /* if */ + } /* if */ + + else /* probably shouldn't fail, but just in case... */ + { + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* else */ + + return(retval); +} /* convertCFString */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + ProcessSerialNumber psn = { 0, kCurrentProcess }; + struct stat statbuf; + FSRef fsref; + CFRange cfrange; + CFURLRef cfurl = NULL; + CFStringRef cfstr = NULL; + CFMutableStringRef cfmutstr = NULL; + char *retval = NULL; + char *cstr = NULL; + int rc = 0; + + BAIL_IF_MACRO(GetProcessBundleLocation(&psn, &fsref) != noErr, NULL, NULL); + cfurl = CFURLCreateFromFSRef(cfallocator, &fsref); + BAIL_IF_MACRO(cfurl == NULL, NULL, NULL); + cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle); + CFRelease(cfurl); + BAIL_IF_MACRO(cfstr == NULL, NULL, NULL); + cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr); + CFRelease(cfstr); + BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL); + + /* we have to decide if we got a binary's path, or the .app dir... */ + cstr = convertCFString(cfmutstr); + if (cstr == NULL) + { + CFRelease(cfmutstr); + return(NULL); + } /* if */ + + rc = stat(cstr, &statbuf); + allocator.Free(cstr); /* done with this. */ + if (rc == -1) + { + CFRelease(cfmutstr); + return(NULL); /* maybe default behaviour will work? */ + } /* if */ + + if (S_ISREG(statbuf.st_mode)) + { + /* Find last dirsep so we can chop the filename from the path. */ + cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards); + if (cfrange.location == kCFNotFound) + { + assert(0); /* shouldn't ever hit this... */ + CFRelease(cfmutstr); + return(NULL); + } /* if */ + + /* chop the "/exename" from the end of the path string... */ + cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location; + CFStringDelete(cfmutstr, cfrange); + + /* If we're an Application Bundle, chop everything but the base. */ + cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"), + kCFCompareCaseInsensitive | + kCFCompareBackwards | + kCFCompareAnchored); + + if (cfrange.location != kCFNotFound) + CFStringDelete(cfmutstr, cfrange); /* chop that, too. */ + } /* if */ + + retval = convertCFString(cfmutstr); + CFRelease(cfmutstr); + + return(retval); /* whew. */ +} /* __PHYSFS_platformCalcBaseDir */ + + +/* !!! FIXME */ +#define osxerr(x) x + +char *__PHYSFS_platformRealPath(const char *path) +{ + /* The symlink and relative path resolving happens in FSPathMakeRef() */ + FSRef fsref; + CFURLRef cfurl = NULL; + CFStringRef cfstr = NULL; + char *retval = NULL; + OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL)); + BAIL_IF_MACRO(rc != noErr, NULL, NULL); + + /* Now get it to spit out a full path. */ + cfurl = CFURLCreateFromFSRef(cfallocator, &fsref); + BAIL_IF_MACRO(cfurl == NULL, ERR_OUT_OF_MEMORY, NULL); + cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle); + CFRelease(cfurl); + BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL); + retval = convertCFString(cfstr); + CFRelease(cfstr); + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return(__PHYSFS_platformRealPath(".")); /* let CFURL sort it out. */ +} /* __PHYSFS_platformCurrentDir */ + + +/* Platform allocator uses default CFAllocator at PHYSFS_init() time. */ + +static CFAllocatorRef cfallocdef = NULL; + +static int macosxAllocatorInit(void) +{ + int retval = 0; + cfallocdef = CFAllocatorGetDefault(); + retval = (cfallocdef != NULL); + if (retval) + CFRetain(cfallocdef); + return(retval); +} /* macosxAllocatorInit */ + + +static void macosxAllocatorDeinit(void) +{ + if (cfallocdef != NULL) + { + CFRelease(cfallocdef); + cfallocdef = NULL; + } /* if */ +} /* macosxAllocatorDeinit */ + + +static void *macosxAllocatorMalloc(PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0)); +} /* macosxAllocatorMalloc */ + + +static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0)); +} /* macosxAllocatorRealloc */ + + +static void macosxAllocatorFree(void *ptr) +{ + CFAllocatorDeallocate(cfallocdef, ptr); +} /* macosxAllocatorFree */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + allocator.Init = macosxAllocatorInit; + allocator.Deinit = macosxAllocatorDeinit; + allocator.Malloc = macosxAllocatorMalloc; + allocator.Realloc = macosxAllocatorRealloc; + allocator.Free = macosxAllocatorFree; + return(1); /* return non-zero: we're supplying custom allocator. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + return( (void *) ((size_t) MPCurrentTaskID()) ); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + MPCriticalRegionID m = NULL; + if (osxerr(MPCreateCriticalRegion(&m)) != noErr) + return NULL; + return m; +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + MPDeleteCriticalRegion(m); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + if (MPEnterCriticalRegion(m, kDurationForever) != noErr) + return(0); + return(1); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + MPExitCriticalRegion(m); +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* PHYSFS_PLATFORM_MACOSX */ + +/* end of macosx.c ... */ + diff --git a/3rdparty/physfs/platform/os2.c b/3rdparty/physfs/platform/os2.c new file mode 100644 index 0000000..3372ca3 --- /dev/null +++ b/3rdparty/physfs/platform/os2.c @@ -0,0 +1,702 @@ +/* + * OS/2 support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_OS2 + +#define INCL_DOSSEMAPHORES +#define INCL_DOSDATETIME +#define INCL_DOSFILEMGR +#define INCL_DOSMODULEMGR +#define INCL_DOSERRORS +#define INCL_DOSPROCESS +#define INCL_DOSDEVICES +#define INCL_DOSDEVIOCTL +#define INCL_DOSMISC +#include + +#include +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + +const char *__PHYSFS_platformDirSeparator = "\\"; + + +static const char *get_os2_error_string(APIRET rc) +{ + switch (rc) + { + case NO_ERROR: return(NULL); /* not an error. */ + case ERROR_INTERRUPT: return(NULL); /* not an error. */ + case ERROR_TIMEOUT: return(NULL); /* not an error. */ + case ERROR_NOT_ENOUGH_MEMORY: return(ERR_OUT_OF_MEMORY); + case ERROR_FILE_NOT_FOUND: return(ERR_NO_SUCH_FILE); + case ERROR_PATH_NOT_FOUND: return(ERR_NO_SUCH_PATH); + case ERROR_ACCESS_DENIED: return(ERR_ACCESS_DENIED); + case ERROR_NOT_DOS_DISK: return(ERR_NOT_A_DOS_DISK); + case ERROR_SHARING_VIOLATION: return(ERR_SHARING_VIOLATION); + case ERROR_CANNOT_MAKE: return(ERR_CANNOT_MAKE); + case ERROR_DEVICE_IN_USE: return(ERR_DEV_IN_USE); + case ERROR_OPEN_FAILED: return(ERR_OPEN_FAILED); + case ERROR_DISK_FULL: return(ERR_DISK_FULL); + case ERROR_PIPE_BUSY: return(ERR_PIPE_BUSY); + case ERROR_SHARING_BUFFER_EXCEEDED: return(ERR_SHARING_BUF_EXCEEDED); + case ERROR_FILENAME_EXCED_RANGE: return(ERR_BAD_FILENAME); + case ERROR_META_EXPANSION_TOO_LONG: return(ERR_BAD_FILENAME); + case ERROR_TOO_MANY_HANDLES: return(ERR_TOO_MANY_HANDLES); + case ERROR_TOO_MANY_OPEN_FILES: return(ERR_TOO_MANY_HANDLES); + case ERROR_NO_MORE_SEARCH_HANDLES: return(ERR_TOO_MANY_HANDLES); + case ERROR_SEEK_ON_DEVICE: return(ERR_SEEK_ERROR); + case ERROR_NEGATIVE_SEEK: return(ERR_SEEK_OUT_OF_RANGE); + /*!!! FIXME: Where did this go? case ERROR_DEL_CURRENT_DIRECTORY: return(ERR_DEL_CWD);*/ + case ERROR_WRITE_PROTECT: return(ERR_WRITE_PROTECT_ERROR); + case ERROR_WRITE_FAULT: return(ERR_WRITE_FAULT); + case ERROR_LOCK_VIOLATION: return(ERR_LOCK_VIOLATION); + case ERROR_GEN_FAILURE: return(ERR_GEN_FAILURE); + case ERROR_UNCERTAIN_MEDIA: return(ERR_UNCERTAIN_MEDIA); + case ERROR_PROTECTION_VIOLATION: return(ERR_PROT_VIOLATION); + case ERROR_BROKEN_PIPE: return(ERR_BROKEN_PIPE); + + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_NAME: + case ERROR_INVALID_DRIVE: + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_FUNCTION: + case ERROR_INVALID_LEVEL: + case ERROR_INVALID_CATEGORY: + case ERROR_DUPLICATE_NAME: + case ERROR_BUFFER_OVERFLOW: + case ERROR_BAD_LENGTH: + case ERROR_BAD_DRIVER_LEVEL: + case ERROR_DIRECT_ACCESS_HANDLE: + case ERROR_NOT_OWNER: + return(ERR_PHYSFS_BAD_OS_CALL); + + default: return(ERR_OS2_GENERIC); + } /* switch */ + + return(NULL); +} /* get_os2_error_string */ + + +static APIRET os2err(APIRET retval) +{ + char buf[128]; + const char *err = get_os2_error_string(retval); + if (err == ERR_OS2_GENERIC) + { + snprintf(buf, sizeof (buf), ERR_OS2_GENERIC, (int) retval); + err = buf; + } /* if */ + + if (err != NULL) + __PHYSFS_setError(err); + + return(retval); +} /* os2err */ + + +/* (be gentle, this function isn't very robust.) */ +static void cvt_path_to_correct_case(char *buf) +{ + char *fname = buf + 3; /* point to first element. */ + char *ptr = strchr(fname, '\\'); /* find end of first element. */ + + buf[0] = toupper(buf[0]); /* capitalize drive letter. */ + + /* + * Go through each path element, and enumerate its parent dir until + * a case-insensitive match is found. If one is (and it SHOULD be) + * then overwrite the original element with the correct case. + * If there's an error, or the path has vanished for some reason, it + * won't hurt to have the original case, so we just keep going. + */ + while (fname != NULL) + { + char spec[CCHMAXPATH]; + FILEFINDBUF3 fb; + HDIR hdir = HDIR_CREATE; + ULONG count = 1; + APIRET rc; + + *(fname - 1) = '\0'; /* isolate parent dir string. */ + + strcpy(spec, buf); /* copy isolated parent dir... */ + strcat(spec, "\\*.*"); /* ...and add wildcard search spec. */ + + if (ptr != NULL) /* isolate element to find (fname is the start). */ + *ptr = '\0'; + + rc = DosFindFirst(spec, &hdir, FILE_DIRECTORY, + &fb, sizeof (fb), &count, FIL_STANDARD); + if (rc == NO_ERROR) + { + while (count == 1) /* while still entries to enumerate... */ + { + if (__PHYSFS_stricmpASCII(fb.achName, fname) == 0) + { + strcpy(fname, fb.achName); + break; /* there it is. Overwrite and stop searching. */ + } /* if */ + + DosFindNext(hdir, &fb, sizeof (fb), &count); + } /* while */ + DosFindClose(hdir); + } /* if */ + + *(fname - 1) = '\\'; /* unisolate parent dir. */ + fname = ptr; /* point to next element. */ + if (ptr != NULL) + { + *ptr = '\\'; /* unisolate element. */ + ptr = strchr(++fname, '\\'); /* find next element. */ + } /* if */ + } /* while */ +} /* cvt_file_to_correct_case */ + + +static char *baseDir = NULL; + +int __PHYSFS_platformInit(void) +{ + char buf[CCHMAXPATH]; + APIRET rc; + PTIB ptib; + PPIB ppib; + PHYSFS_sint32 len; + + assert(baseDir == NULL); + BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, NULL, 0); + rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0); + + /* chop off filename, leave path. */ + for (len = strlen(buf) - 1; len >= 0; len--) + { + if (buf[len] == '\\') + { + buf[len] = '\0'; + break; + } /* if */ + } /* for */ + + assert(len > 0); /* should have been a "x:\\" on the front on string. */ + + /* The string is capitalized! Figure out the REAL case... */ + cvt_path_to_correct_case(buf); + + baseDir = (char *) allocator.Malloc(len + 1); + BAIL_IF_MACRO(baseDir == NULL, ERR_OUT_OF_MEMORY, 0); + strcpy(baseDir, buf); + return(1); /* success. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + assert(baseDir != NULL); + allocator.Free(baseDir); + baseDir = NULL; + return(1); /* success. */ +} /* __PHYSFS_platformDeinit */ + + +static int disc_is_inserted(ULONG drive) +{ + int rc; + char buf[20]; + DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION); + rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf)); + DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION); + return(rc == NO_ERROR); +} /* is_cdrom_inserted */ + + +/* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */ +#define CD01 0x31304443 + +static int is_cdrom_drive(ULONG drive) +{ + PHYSFS_uint32 param, data; + ULONG ul1, ul2; + APIRET rc; + HFILE hfile = NULLHANDLE; + char drivename[3] = { 'A' + drive, ':', '\0' }; + + rc = DosOpen(drivename, &hfile, &ul1, 0, 0, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, + OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL); + BAIL_IF_MACRO(rc != NO_ERROR, NULL, 0); + + data = 0; + param = PHYSFS_swapULE32(CD01); + ul1 = ul2 = sizeof (PHYSFS_uint32); + rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER, + ¶m, sizeof (param), &ul1, &data, sizeof (data), &ul2); + + DosClose(hfile); + return((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01)); +} /* is_cdrom_drive */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + ULONG dummy = 0; + ULONG drivemap = 0; + ULONG i, bit; + APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap); + if (os2err(rc) != NO_ERROR) + return; + + for (i = 0, bit = 1; i < 26; i++, bit <<= 1) + { + if (drivemap & bit) /* this logical drive exists. */ + { + if ((is_cdrom_drive(i)) && (disc_is_inserted(i))) + { + char drive[4] = "x:\\"; + drive[0] = ('A' + i); + cb(data, drive); + } /* if */ + } /* if */ + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + char *retval = (char *) allocator.Malloc(strlen(baseDir) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, baseDir); /* calculated at init time. */ + return(retval); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + return(NULL); /* (*shrug*) */ +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + return(__PHYSFS_platformCalcBaseDir(NULL)); +} /* __PHYSFS_platformGetUserDir */ + + +int __PHYSFS_platformExists(const char *fname) +{ + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + return(os2err(rc) == NO_ERROR); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + return(0); /* no symlinks in OS/2. */ +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0) + return((fs.attrFile & FILE_DIRECTORY) != 0); +} /* __PHYSFS_platformIsDirectory */ + + +/* !!! FIXME: can we lose the malloc here? */ +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + char spec[CCHMAXPATH]; + FILEFINDBUF3 fb; + HDIR hdir = HDIR_CREATE; + ULONG count = 1; + APIRET rc; + + if (strlen(dirname) > sizeof (spec) - 5) + { + __PHYSFS_setError(ERR_BAD_FILENAME); + return; + } /* if */ + + strcpy(spec, dirname); + strcat(spec, (spec[strlen(spec) - 1] != '\\') ? "\\*.*" : "*.*"); + + rc = DosFindFirst(spec, &hdir, + FILE_DIRECTORY | FILE_ARCHIVED | + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM, + &fb, sizeof (fb), &count, FIL_STANDARD); + + if (os2err(rc) != NO_ERROR) + return; + + while (count == 1) + { + if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0)) + callback(callbackdata, origdir, fb.achName); + + DosFindNext(hdir, &fb, sizeof (fb), &count); + } /* while */ + + DosFindClose(hdir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + char *retval; + ULONG currentDisk; + ULONG dummy; + ULONG pathSize = 0; + APIRET rc; + BYTE byte; + + rc = DosQueryCurrentDisk(¤tDisk, &dummy); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL); + + /* The first call just tells us how much space we need for the string. */ + rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize); + pathSize++; /* Add space for null terminator. */ + retval = (char *) allocator.Malloc(pathSize + 3); /* plus "x:\\" */ + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* Actually get the string this time. */ + rc = DosQueryCurrentDir(currentDisk, (PBYTE) (retval + 3), &pathSize); + if (os2err(rc) != NO_ERROR) + { + allocator.Free(retval); + return(NULL); + } /* if */ + + retval[0] = ('A' + (currentDisk - 1)); + retval[1] = ':'; + retval[2] = '\\'; + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + char buf[CCHMAXPATH]; + char *retval; + APIRET rc = DosQueryPathInfo(path, FIL_QUERYFULLNAME, buf, sizeof (buf)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL); + retval = (char *) allocator.Malloc(strlen(buf) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, buf); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + return(os2err(DosCreateDir(path, NULL)) == NO_ERROR); +} /* __PHYSFS_platformMkDir */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + ULONG actionTaken = 0; + HFILE hfile = NULLHANDLE; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | + OPEN_ACCESS_READONLY, NULL)); + + return((void *) hfile); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + ULONG actionTaken = 0; + HFILE hfile = NULLHANDLE; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL, + OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | + OPEN_ACCESS_READWRITE, NULL)); + + return((void *) hfile); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + ULONG dummy = 0; + HFILE hfile = NULLHANDLE; + APIRET rc; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + rc = os2err(DosOpen(filename, &hfile, &dummy, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | + OPEN_ACCESS_READWRITE, NULL)); + + if (rc == NO_ERROR) + { + if (os2err(DosSetFilePtr(hfile, 0, FILE_END, &dummy)) != NO_ERROR) + { + DosClose(hfile); + hfile = NULLHANDLE; + } /* if */ + } /* if */ + + return((void *) hfile); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HFILE hfile = (HFILE) opaque; + PHYSFS_sint64 retval; + ULONG br; + + for (retval = 0; retval < count; retval++) + { + os2err(DosRead(hfile, buffer, size, &br)); + if (br < size) + { + DosSetFilePtr(hfile, -br, FILE_CURRENT, &br); /* try to cleanup. */ + return(retval); + } /* if */ + + buffer = (void *) ( ((char *) buffer) + size ); + } /* for */ + + return(retval); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HFILE hfile = (HFILE) opaque; + PHYSFS_sint64 retval; + ULONG bw; + + for (retval = 0; retval < count; retval++) + { + os2err(DosWrite(hfile, buffer, size, &bw)); + if (bw < size) + { + DosSetFilePtr(hfile, -bw, FILE_CURRENT, &bw); /* try to cleanup. */ + return(retval); + } /* if */ + + buffer = (void *) ( ((char *) buffer) + size ); + } /* for */ + + return(retval); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + ULONG dummy; + HFILE hfile = (HFILE) opaque; + LONG dist = (LONG) pos; + + /* hooray for 32-bit filesystem limits! :) */ + BAIL_IF_MACRO((PHYSFS_uint64) dist != pos, ERR_SEEK_OUT_OF_RANGE, 0); + + return(os2err(DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy)) == NO_ERROR); +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + ULONG pos; + HFILE hfile = (HFILE) opaque; + APIRET rc = os2err(DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos)); + BAIL_IF_MACRO(rc != NO_ERROR, NULL, -1); + return((PHYSFS_sint64) pos); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + FILESTATUS3 fs; + HFILE hfile = (HFILE) opaque; + APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1); + return((PHYSFS_sint64) fs.cbFile); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 len, pos; + + len = __PHYSFS_platformFileLength(opaque); + BAIL_IF_MACRO(len == -1, NULL, 1); /* (*shrug*) */ + pos = __PHYSFS_platformTell(opaque); + BAIL_IF_MACRO(pos == -1, NULL, 1); /* (*shrug*) */ + + return(pos >= len); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + return(os2err(DosResetBuffer((HFILE) opaque)) == NO_ERROR); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + return(os2err(DosClose((HFILE) opaque)) == NO_ERROR); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + if (__PHYSFS_platformIsDirectory(path)) + return(os2err(DosDeleteDir(path)) == NO_ERROR); + + return(os2err(DosDelete(path)) == NO_ERROR); +} /* __PHYSFS_platformDelete */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + PHYSFS_sint64 retval; + struct tm tm; + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1); + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2; + tm.tm_min = fs.ftimeLastWrite.minutes; + tm.tm_hour = fs.ftimeLastWrite.hours; + tm.tm_mday = fs.fdateLastWrite.day; + tm.tm_mon = fs.fdateLastWrite.month; + tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + return(retval); +} /* __PHYSFS_platformGetLastModTime */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + PTIB ptib; + PPIB ppib; + + /* + * Allegedly, this API never fails, but we'll punt and return a + * default value (zero might as well do) if it does. + */ + BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, 0, 0); + return((void *) ptib->tib_ordinal); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + HMTX hmtx = NULLHANDLE; + os2err(DosCreateMutexSem(NULL, &hmtx, 0, 0)); + return((void *) hmtx); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + DosCloseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + /* Do _NOT_ call os2err() (which sets the physfs error msg) in here! */ + return(DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + DosReleaseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_OS2 */ + +/* end of os2.c ... */ + diff --git a/3rdparty/physfs/platform/pocketpc.c b/3rdparty/physfs/platform/pocketpc.c new file mode 100644 index 0000000..c49c0cf --- /dev/null +++ b/3rdparty/physfs/platform/pocketpc.c @@ -0,0 +1,612 @@ +/* + * PocketPC support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_POCKETPC + +#include +#include + +#include "physfs_internal.h" + +#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF +typedef struct +{ + HANDLE handle; + int readonly; +} winCEfile; + + +const char *__PHYSFS_platformDirSeparator = "\\"; +static char *userDir = NULL; + +/* + * Figure out what the last failing Win32 API call was, and + * generate a human-readable string for the error message. + * + * The return value is a static buffer that is overwritten with + * each call to this function. + */ +static const char *win32strerror(void) +{ + static TCHAR msgbuf[255]; + TCHAR *ptr = msgbuf; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + msgbuf, + sizeof (msgbuf) / sizeof (TCHAR), + NULL + ); + + /* chop off newlines. */ + for (ptr = msgbuf; *ptr; ptr++) + { + if ((*ptr == '\n') || (*ptr == '\r')) + { + *ptr = ' '; + break; + } /* if */ + } /* for */ + + return((const char *) msgbuf); +} /* win32strerror */ + + +/* !!! FIXME: need to check all of these for NULLs. */ +#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \ + if (str == NULL) \ + w_assignto = NULL; \ + else { \ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \ + w_assignto = (char *) __PHYSFS_smallAlloc(len); \ + PHYSFS_uc2fromutf8(str, (PHYSFS_uint16 *) w_assignto, len); \ + } \ +} \ + + +static char *getExePath() +{ + DWORD buflen; + int success = 0; + TCHAR *ptr = NULL; + TCHAR *retval = (TCHAR*) allocator.Malloc(sizeof (TCHAR) * (MAX_PATH + 1)); + char *charretval; + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + retval[0] = _T('\0'); + /* !!! FIXME: don't preallocate here? */ + /* !!! FIXME: use smallAlloc? */ + buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1); + if (buflen <= 0) + __PHYSFS_setError(win32strerror()); + else + { + retval[buflen] = '\0'; /* does API always null-terminate this? */ + ptr = retval+buflen; + while( ptr != retval ) + { + if( *ptr != _T('\\') ) + *ptr-- = _T('\0'); + else + break; + } /* while */ + success = 1; + } /* else */ + + if (!success) + { + allocator.Free(retval); + return(NULL); /* physfs error message will be set, above. */ + } /* if */ + + buflen = (buflen * 4) + 1; + charretval = (char *) allocator.Malloc(buflen); + if (charretval != NULL) + PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) retval, charretval, buflen); + allocator.Free(retval); + return(charretval); /* w00t. */ +} /* getExePath */ + + +int __PHYSFS_platformInit(void) +{ + userDir = getExePath(); + BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* failed? */ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + allocator.Free(userDir); + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + /* no-op on this platform. */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + return(getExePath()); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + return userDir; + BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL); +} /* __PHYSFS_platformGetUserDir */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + return((void *)1); /* single threaded. */ +} /* __PHYSFS_platformGetThreadID */ + + +int __PHYSFS_platformExists(const char *fname) +{ + int retval = 0; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + if (w_fname != NULL) + retval = (GetFileAttributes(w_fname) != INVALID_FILE_ATTRIBUTES); + __PHYSFS_smallFree(w_fname); + + return(retval); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, 0); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + int retval = 0; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + if (w_fname != NULL) + retval = ((GetFileAttributes(w_fname) & FILE_ATTRIBUTE_DIRECTORY) != 0); + __PHYSFS_smallFree(w_fname); + + return(retval); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +static int doEnumCallback(const wchar_t *w_fname) +{ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((wcslen(w_fname) * 4) + 1); + char *str = (char *) __PHYSFS_smallAlloc(len); + PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) w_fname, str, len); + callback(callbackdata, origdir, str); + __PHYSFS_smallFree(str); + return 1; +} /* doEnumCallback */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + HANDLE dir; + WIN32_FIND_DATA ent; + char *SearchPath; + wchar_t *w_SearchPath; + size_t len = strlen(dirname); + + /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ + SearchPath = (char *) __PHYSFS_smallAlloc(len + 3); + BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* Copy current dirname */ + strcpy(SearchPath, dirname); + + /* if there's no '\\' at the end of the path, stick one in there. */ + if (SearchPath[len - 1] != '\\') + { + SearchPath[len++] = '\\'; + SearchPath[len] = '\0'; + } /* if */ + + /* Append the "*" to the end of the string */ + strcat(SearchPath, "*"); + + UTF8_TO_UNICODE_STACK_MACRO(w_SearchPath, SearchPath); + __PHYSFS_smallFree(SearchPath); + dir = FindFirstFile(w_SearchPath, &ent); + __PHYSFS_smallFree(w_SearchPath); + + if (dir == INVALID_HANDLE_VALUE) + return; + + do + { + const char *str = NULL; + + if (wcscmp(ent.cFileName, L".") == 0) + continue; + + if (wcscmp(ent.cFileName, L"..") == 0) + continue; + + if (!doEnumCallback(ent.cFileName)) + break; + } while (FindNextFile(dir, &ent) != 0); + + FindClose(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return("\\"); +} /* __PHYSFS_platformCurrentDir */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + char *retval = (char *) allocator.Malloc(strlen(path) + 1); + strcpy(retval,path); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + int retval = 0; + wchar_t *w_path = NULL; + UTF8_TO_UNICODE_STACK_MACRO(w_path, path); + if (w_path != NULL) + { + retval = CreateDirectory(w_path, NULL); + __PHYSFS_smallFree(w_fname); + } /* if */ + return(retval); +} /* __PHYSFS_platformMkDir */ + + +static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly) +{ + HANDLE fileHandle; + winCEfile *retval; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + fileHandle = CreateFile(w_fname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + __PHYSFS_smallFree(w_fname); + + BAIL_IF_MACRO(fileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL); + + retval = (winCEfile *) allocator.Malloc(sizeof (winCEfile)); + if (retval == NULL) + { + CloseHandle(fileHandle); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval->readonly = rdonly; + retval->handle = fileHandle; + return(retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1)); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0)); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0); + if (retval != NULL) + { + HANDLE h = ((winCEfile *) retval)->handle; + if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + { + const char *err = win32strerror(); + CloseHandle(h); + allocator.Free(retval); + BAIL_MACRO(err, NULL); + } /* if */ + } /* if */ + + return(retval); + +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD CountOfBytesRead; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /*!!! - uint32 might be a greater # than DWORD */ + if (!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL)) + { + retval = -1; + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! - What if not the right amount of bytes was read to make an object? */ + retval = CountOfBytesRead / size; + } /* else */ + + return(retval); + +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD CountOfBytesWritten; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /*!!! - uint32 might be a greater # than DWORD */ + if (!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL)) + { + retval = -1; + } /* if */ + else + { + /* Return the number of "objects" read. */ + /*!!! - What if not the right number of bytes was written? */ + retval = CountOfBytesWritten / size; + } /* else */ + + return(retval); + +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD HighOrderPos; + DWORD rc; + + /* Get the high order 32-bits of the position */ + //HighOrderPos = HIGHORDER_UINT64(pos); + HighOrderPos = (unsigned long)(pos>>32); + + /*!!! SetFilePointer needs a signed 64-bit value. */ + /* Move pointer "pos" count from start of file */ + rc = SetFilePointer(Handle, (unsigned long)(pos&0x00000000ffffffff), + &HighOrderPos, FILE_BEGIN); + + if ((rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), 0); + } + + return(1); /* No error occured */ +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD HighPos = 0; + DWORD LowPos; + PHYSFS_sint64 retval; + + /* Get current position */ + LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT); + if ((LowPos == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos; + //assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD SizeHigh; + DWORD SizeLow; + PHYSFS_sint64 retval; + + SizeLow = GetFileSize(Handle, &SizeHigh); + if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow; + //assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + const PHYSFS_sint64 FileLength = __PHYSFS_platformFileLength(opaque); + PHYSFS_sint64 FilePosition; + int retval = 0; + + if (FileLength == 0) + return 1; /* we're definitely at EOF. */ + + /* Get the current position in the file */ + if ((FilePosition = __PHYSFS_platformTell(opaque)) != -1) + { + /* Non-zero if EOF is equal to the file length */ + retval = (FilePosition == FileLength); + } /* if */ + + return(retval); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + winCEfile *fh = ((winCEfile *) opaque); + if (!fh->readonly) + BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), win32strerror(), 0); + + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + BAIL_IF_MACRO(!CloseHandle(Handle), win32strerror(), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + wchar_t *w_path = NULL; + UTF8_TO_UNICODE_STACK_MACRO(w_path, path); + + /* If filename is a folder */ + if (GetFileAttributes(w_path) == FILE_ATTRIBUTE_DIRECTORY) + { + int retval = !RemoveDirectory(w_path); + __PHYSFS_smallFree(w_path); + BAIL_IF_MACRO(retval, win32strerror(), 0); + } /* if */ + else + { + int retval = !DeleteFile(w_path); + __PHYSFS_smallFree(w_path); + BAIL_IF_MACRO(retval, win32strerror(), 0); + } /* else */ + + return(1); /* if you got here, it worked. */ +} /* __PHYSFS_platformDelete */ + + +/* + * !!! FIXME: why aren't we using Critical Sections instead of Mutexes? + * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are + * !!! FIXME: mutexes for threads in a single process and are faster. + */ +void *__PHYSFS_platformCreateMutex(void) +{ + return((void *) CreateMutex(NULL, FALSE, NULL)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + CloseHandle((HANDLE) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ReleaseMutex((HANDLE) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, -1); +} /* __PHYSFS_platformGetLastModTime */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_POCKETPC */ + +/* end of pocketpc.c ... */ + diff --git a/3rdparty/physfs/platform/posix.c b/3rdparty/physfs/platform/posix.c new file mode 100644 index 0000000..85568ea --- /dev/null +++ b/3rdparty/physfs/platform/posix.c @@ -0,0 +1,425 @@ +/* + * Posix-esque support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_POSIX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PHYSFS_HAVE_LLSEEK +#include +#endif + +#include "physfs_internal.h" + + +const char *__PHYSFS_platformDirSeparator = "/"; + + +char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname) +{ + const char *envr = getenv(varname); + char *retval = NULL; + + if (envr != NULL) + { + retval = (char *) allocator.Malloc(strlen(envr) + 1); + if (retval != NULL) + strcpy(retval, envr); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCopyEnvironmentVariable */ + + +static char *getUserNameByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_name != NULL)) + { + retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_name); + } /* if */ + + return(retval); +} /* getUserNameByUID */ + + +static char *getUserDirByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_dir != NULL)) + { + retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_dir); + } /* if */ + + return(retval); +} /* getUserDirByUID */ + + +char *__PHYSFS_platformGetUserName(void) +{ + char *retval = getUserNameByUID(); + if (retval == NULL) + retval = __PHYSFS_platformCopyEnvironmentVariable("USER"); + return(retval); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME"); + + /* if the environment variable was set, make sure it's really a dir. */ + if (retval != NULL) + { + struct stat statbuf; + if ((stat(retval, &statbuf) == -1) || (S_ISDIR(statbuf.st_mode) == 0)) + { + allocator.Free(retval); + retval = NULL; + } /* if */ + } /* if */ + + if (retval == NULL) + retval = getUserDirByUID(); + + return(retval); +} /* __PHYSFS_platformGetUserDir */ + + +int __PHYSFS_platformExists(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0); + return( (S_ISLNK(statbuf.st_mode)) ? 1 : 0 ); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(stat(fname, &statbuf) == -1, strerror(errno), 0); + return( (S_ISDIR(statbuf.st_mode)) ? 1 : 0 ); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* platform-independent notation is Unix-style already. :) */ + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + DIR *dir; + struct dirent *ent; + int bufsize = 0; + char *buf = NULL; + int dlen = 0; + + if (omitSymLinks) /* !!! FIXME: this malloc sucks. */ + { + dlen = strlen(dirname); + bufsize = dlen + 256; + buf = (char *) allocator.Malloc(bufsize); + if (buf == NULL) + return; + strcpy(buf, dirname); + if (buf[dlen - 1] != '/') + { + buf[dlen++] = '/'; + buf[dlen] = '\0'; + } /* if */ + } /* if */ + + errno = 0; + dir = opendir(dirname); + if (dir == NULL) + { + allocator.Free(buf); + return; + } /* if */ + + while ((ent = readdir(dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0) + continue; + + if (strcmp(ent->d_name, "..") == 0) + continue; + + if (omitSymLinks) + { + char *p; + int len = strlen(ent->d_name) + dlen + 1; + if (len > bufsize) + { + p = (char *) allocator.Realloc(buf, len); + if (p == NULL) + continue; + buf = p; + bufsize = len; + } /* if */ + + strcpy(buf + dlen, ent->d_name); + if (__PHYSFS_platformIsSymLink(buf)) + continue; + } /* if */ + + callback(callbackdata, origdir, ent->d_name); + } /* while */ + + allocator.Free(buf); + closedir(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + int rc; + errno = 0; + rc = mkdir(path, S_IRWXU); + BAIL_IF_MACRO(rc == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformMkDir */ + + +static void *doOpen(const char *filename, int mode) +{ + const int appending = (mode & O_APPEND); + int fd; + int *retval; + errno = 0; + + /* O_APPEND doesn't actually behave as we'd like. */ + mode &= ~O_APPEND; + + fd = open(filename, mode, S_IRUSR | S_IWUSR); + BAIL_IF_MACRO(fd < 0, strerror(errno), NULL); + + if (appending) + { + if (lseek(fd, 0, SEEK_END) < 0) + { + close(fd); + BAIL_MACRO(strerror(errno), NULL); + } /* if */ + } /* if */ + + retval = (int *) allocator.Malloc(sizeof (int)); + if (retval == NULL) + { + close(fd); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + *retval = fd; + return((void *) retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return(doOpen(filename, O_RDONLY)); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return(doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC)); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + return(doOpen(filename, O_WRONLY | O_CREAT | O_APPEND)); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + int fd = *((int *) opaque); + int max = size * count; + int rc = read(fd, buffer, max); + + BAIL_IF_MACRO(rc == -1, strerror(errno), rc); + assert(rc <= max); + + if ((rc < max) && (size > 1)) + lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ + + return(rc / size); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + int fd = *((int *) opaque); + int max = size * count; + int rc = write(fd, (void *) buffer, max); + + BAIL_IF_MACRO(rc == -1, strerror(errno), rc); + assert(rc <= max); + + if ((rc < max) && (size > 1)) + lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ + + return(rc / size); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + int fd = *((int *) opaque); + + #ifdef PHYSFS_HAVE_LLSEEK + unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF); + unsigned long offset_low = (pos & 0xFFFFFFFF); + loff_t retoffset; + int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET); + BAIL_IF_MACRO(rc == -1, strerror(errno), 0); + #else + BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0); + #endif + + return(1); +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + int fd = *((int *) opaque); + PHYSFS_sint64 retval; + + #ifdef PHYSFS_HAVE_LLSEEK + loff_t retoffset; + int rc = llseek(fd, 0, &retoffset, SEEK_CUR); + BAIL_IF_MACRO(rc == -1, strerror(errno), -1); + retval = (PHYSFS_sint64) retoffset; + #else + retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + #endif + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + int fd = *((int *) opaque); + struct stat statbuf; + BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1); + return((PHYSFS_sint64) statbuf.st_size); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 pos = __PHYSFS_platformTell(opaque); + PHYSFS_sint64 len = __PHYSFS_platformFileLength(opaque); + return((pos < 0) || (len < 0) || (pos >= len)); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + int fd = *((int *) opaque); + if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) + BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + int fd = *((int *) opaque); + BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformDelete */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(stat(fname, &statbuf) < 0, strerror(errno), -1); + return statbuf.st_mtime; +} /* __PHYSFS_platformGetLastModTime */ + +#endif /* PHYSFS_PLATFORM_POSIX */ + +/* end of posix.c ... */ + diff --git a/3rdparty/physfs/platform/unix.c b/3rdparty/physfs/platform/unix.c new file mode 100644 index 0000000..093e7eb --- /dev/null +++ b/3rdparty/physfs/platform/unix.c @@ -0,0 +1,431 @@ +/* + * Unix support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_UNIX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (!defined PHYSFS_NO_THREAD_SUPPORT) +#include +#endif + +#ifdef PHYSFS_HAVE_SYS_UCRED_H +# ifdef PHYSFS_HAVE_MNTENT_H +# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */ +# endif +# include +# include +#endif + +#ifdef PHYSFS_HAVE_MNTENT_H +#include +#endif + +#include "physfs_internal.h" + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +int __PHYSFS_platformInit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +#ifdef PHYSFS_NO_CDROM_SUPPORT + +/* Stub version for platforms without CD-ROM support. */ +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ +} /* __PHYSFS_platformDetectAvailableCDs */ + +#elif (defined PHYSFS_HAVE_SYS_UCRED_H) + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + int i; + struct statfs *mntbufp = NULL; + int mounts = getmntinfo(&mntbufp, MNT_WAIT); + + for (i = 0; i < mounts; i++) + { + int add_it = 0; + + if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0) + add_it = 1; + else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0) + add_it = 1; + + /* add other mount types here */ + + if (add_it) + cb(data, mntbufp[i].f_mntonname); + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + +#elif (defined PHYSFS_HAVE_MNTENT_H) + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + FILE *mounts = NULL; + struct mntent *ent = NULL; + + mounts = setmntent("/etc/mtab", "r"); + BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/); + + while ( (ent = getmntent(mounts)) != NULL ) + { + int add_it = 0; + if (strcmp(ent->mnt_type, "iso9660") == 0) + add_it = 1; + else if (strcmp(ent->mnt_type, "udf") == 0) + add_it = 1; + + /* !!! FIXME: these might pick up floppy drives, right? */ + else if (strcmp(ent->mnt_type, "auto") == 0) + add_it = 1; + else if (strcmp(ent->mnt_type, "supermount") == 0) + add_it = 1; + + /* add other mount types here */ + + if (add_it) + cb(data, ent->mnt_dir); + } /* while */ + + endmntent(mounts); + +} /* __PHYSFS_platformDetectAvailableCDs */ + +#endif + + +/* this is in posix.c ... */ +extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname); + + +/* + * See where program (bin) resides in the $PATH specified by (envr). + * returns a copy of the first element in envr that contains it, or NULL + * if it doesn't exist or there were other problems. PHYSFS_SetError() is + * called if we have a problem. + * + * (envr) will be scribbled over, and you are expected to allocator.Free() the + * return value when you're done with it. + */ +static char *findBinaryInPath(const char *bin, char *envr) +{ + size_t alloc_size = 0; + char *exe = NULL; + char *start = envr; + char *ptr; + + BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL); + + do + { + size_t size; + ptr = strchr(start, ':'); /* find next $PATH separator. */ + if (ptr) + *ptr = '\0'; + + size = strlen(start) + strlen(bin) + 2; + if (size > alloc_size) + { + char *x = (char *) allocator.Realloc(exe, size); + if (x == NULL) + { + if (exe != NULL) + allocator.Free(exe); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + alloc_size = size; + exe = x; + } /* if */ + + /* build full binary path... */ + strcpy(exe, start); + if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/')) + strcat(exe, "/"); + strcat(exe, bin); + + if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */ + { + strcpy(exe, start); /* i'm lazy. piss off. */ + return(exe); + } /* if */ + + start = ptr + 1; /* start points to beginning of next element. */ + } while (ptr != NULL); + + if (exe != NULL) + allocator.Free(exe); + + return(NULL); /* doesn't exist in path. */ +} /* findBinaryInPath */ + + +static char *readSymLink(const char *path) +{ + ssize_t len = 64; + ssize_t rc = -1; + char *retval = NULL; + + while (1) + { + char *ptr = (char *) allocator.Realloc(retval, (size_t) len); + if (ptr == NULL) + break; /* out of memory. */ + retval = ptr; + + rc = readlink(path, retval, len); + if (rc == -1) + break; /* not a symlink, i/o error, etc. */ + + else if (rc < len) + { + retval[rc] = '\0'; /* readlink doesn't null-terminate. */ + return retval; /* we're good to go. */ + } /* else if */ + + len *= 2; /* grow buffer, try again. */ + } /* while */ + + if (retval != NULL) + allocator.Free(retval); + return NULL; +} /* readSymLink */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + char *retval = NULL; + char *envr = NULL; + + /* fast path: default behaviour can handle this. */ + if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) ) + return(NULL); /* higher level will parse out real path from argv0. */ + + /* + * Try to avoid using argv0 unless forced to. If there's a Linux-like + * /proc filesystem, you can get the full path to the current process from + * the /proc/self/exe symlink. + */ + retval = readSymLink("/proc/self/exe"); + if (retval == NULL) + { + /* older kernels don't have /proc/self ... try PID version... */ + const unsigned long long pid = (unsigned long long) getpid(); + char path[64]; + const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid); + if ( (rc > 0) && (rc < sizeof(path)) ) + retval = readSymLink(path); + } /* if */ + + if (retval != NULL) /* chop off filename. */ + { + char *ptr = strrchr(retval, '/'); + if (ptr != NULL) + *ptr = '\0'; + } /* if */ + + if ((retval == NULL) && (argv0 != NULL)) + { + /* If there's no dirsep on argv0, then look through $PATH for it. */ + envr = __PHYSFS_platformCopyEnvironmentVariable("PATH"); + BAIL_IF_MACRO(!envr, NULL, NULL); + retval = findBinaryInPath(argv0, envr); + allocator.Free(envr); + } /* if */ + + if (retval != NULL) + { + /* try to shrink buffer... */ + char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1); + if (ptr != NULL) + retval = ptr; /* oh well if it failed. */ + } /* if */ + + return(retval); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + char resolved_path[MAXPATHLEN]; + char *retval = NULL; + + errno = 0; + BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL); + retval = (char *) allocator.Malloc(strlen(resolved_path) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, resolved_path); + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + /* + * This can't just do platformRealPath("."), since that would eventually + * just end up calling back into here. + */ + + int allocSize = 0; + char *retval = NULL; + char *ptr; + + do + { + allocSize += 100; + ptr = (char *) allocator.Realloc(retval, allocSize); + if (ptr == NULL) + { + if (retval != NULL) + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval = ptr; + ptr = getcwd(retval, allocSize); + } while (ptr == NULL && errno == ERANGE); + + if (ptr == NULL && errno) + { + /* + * getcwd() failed for some reason, for example current + * directory not existing. + */ + if (retval != NULL) + allocator.Free(retval); + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + + +#if (defined PHYSFS_NO_THREAD_SUPPORT) + +void *__PHYSFS_platformGetThreadID(void) { return((void *) 0x0001); } +void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); } +void __PHYSFS_platformDestroyMutex(void *mutex) {} +int __PHYSFS_platformGrabMutex(void *mutex) { return(1); } +void __PHYSFS_platformReleaseMutex(void *mutex) {} + +#else + +typedef struct +{ + pthread_mutex_t mutex; + pthread_t owner; + PHYSFS_uint32 count; +} PthreadMutex; + +void *__PHYSFS_platformGetThreadID(void) +{ + return( (void *) ((size_t) pthread_self()) ); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + int rc; + PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex)); + BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL); + rc = pthread_mutex_init(&m->mutex, NULL); + if (rc != 0) + { + allocator.Free(m); + BAIL_MACRO(strerror(rc), NULL); + } /* if */ + + m->count = 0; + m->owner = (pthread_t) 0xDEADBEEF; + return((void *) m); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + + /* Destroying a locked mutex is a bug, but we'll try to be helpful. */ + if ((m->owner == pthread_self()) && (m->count > 0)) + pthread_mutex_unlock(&m->mutex); + + pthread_mutex_destroy(&m->mutex); + allocator.Free(m); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + pthread_t tid = pthread_self(); + if (m->owner != tid) + { + if (pthread_mutex_lock(&m->mutex) != 0) + return(0); + m->owner = tid; + } /* if */ + + m->count++; + return(1); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + if (m->owner == pthread_self()) + { + if (--m->count == 0) + { + m->owner = (pthread_t) 0xDEADBEEF; + pthread_mutex_unlock(&m->mutex); + } /* if */ + } /* if */ +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* !PHYSFS_NO_THREAD_SUPPORT */ + +#endif /* PHYSFS_PLATFORM_UNIX */ + +/* end of unix.c ... */ + diff --git a/3rdparty/physfs/platform/windows.c b/3rdparty/physfs/platform/windows.c new file mode 100644 index 0000000..2ced9a4 --- /dev/null +++ b/3rdparty/physfs/platform/windows.c @@ -0,0 +1,1408 @@ +/* + * Windows support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, and made sane by Gregory S. Read. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_WINDOWS + +/* Forcibly disable UNICODE, since we manage this ourselves. */ +#ifdef UNICODE +#undef UNICODE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + +#define LOWORDER_UINT64(pos) ((PHYSFS_uint32) (pos & 0xFFFFFFFF)) +#define HIGHORDER_UINT64(pos) ((PHYSFS_uint32) ((pos >> 32) & 0xFFFFFFFF)) + +/* + * Users without the platform SDK don't have this defined. The original docs + * for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should + * work as desired. + */ +#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF + +/* just in case... */ +#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF + +/* Not defined before the Vista SDK. */ +#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C + + +#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \ + if (str == NULL) \ + w_assignto = NULL; \ + else { \ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \ + w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \ + if (w_assignto != NULL) \ + PHYSFS_utf8ToUcs2(str, (PHYSFS_uint16 *) w_assignto, len); \ + } \ +} \ + +static PHYSFS_uint64 wStrLen(const WCHAR *wstr) +{ + PHYSFS_uint64 len = 0; + while (*(wstr++)) + len++; + return(len); +} /* wStrLen */ + +static char *unicodeToUtf8Heap(const WCHAR *w_str) +{ + char *retval = NULL; + if (w_str != NULL) + { + void *ptr = NULL; + const PHYSFS_uint64 len = (wStrLen(w_str) * 4) + 1; + retval = allocator.Malloc(len); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) w_str, retval, len); + ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */ + if (ptr != NULL) + retval = (char *) ptr; + } /* if */ + return(retval); +} /* unicodeToUtf8Heap */ + + +static char *codepageToUtf8Heap(const char *cpstr) +{ + char *retval = NULL; + if (cpstr != NULL) + { + const int len = (int) (strlen(cpstr) + 1); + WCHAR *wbuf = (WCHAR *) __PHYSFS_smallAlloc(len * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, len, wbuf, len); + retval = (char *) allocator.Malloc(len * 4); + if (retval == NULL) + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + else + PHYSFS_utf8FromUcs2(wbuf, retval, len * 4); + __PHYSFS_smallFree(wbuf); + } /* if */ + return(retval); +} /* codepageToUtf8Heap */ + + +typedef struct +{ + HANDLE handle; + int readonly; +} WinApiFile; + + +static char *userDir = NULL; +static int osHasUnicode = 0; + + +/* pointers for APIs that may not exist on some Windows versions... */ +static HANDLE libKernel32 = NULL; +static HANDLE libUserEnv = NULL; +static HANDLE libAdvApi32 = NULL; +static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD); +static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); +static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD); +static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR); +static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW); +static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW); +static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR); +static BOOL (WINAPI *pDeleteFileW)(LPCWSTR); +static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR); +static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES); +static BOOL (WINAPI *pGetFileAttributesExA) + (LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID); +static BOOL (WINAPI *pGetFileAttributesExW) + (LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID); +static DWORD (WINAPI *pFormatMessageW) + (DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *); +static HANDLE (WINAPI *pCreateFileW) + (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); + + +/* + * Fallbacks for missing Unicode functions on Win95/98/ME. These are filled + * into the function pointers if looking up the real Unicode entry points + * in the system DLLs fails, so they're never used on WinNT/XP/Vista/etc. + * They make an earnest effort to convert to/from UTF-8 and UCS-2 to + * the user's current codepage. + */ + +static BOOL WINAPI fallbackGetUserNameW(LPWSTR buf, LPDWORD len) +{ + const DWORD cplen = *len; + char *cpstr = __PHYSFS_smallAlloc(cplen); + BOOL retval = GetUserNameA(cpstr, len); + if (buf != NULL) + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, cplen, buf, *len); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackGetUserNameW */ + +static DWORD WINAPI fallbackFormatMessageW(DWORD dwFlags, LPCVOID lpSource, + DWORD dwMessageId, DWORD dwLangId, + LPWSTR lpBuf, DWORD nSize, + va_list *Arguments) +{ + char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize); + DWORD retval = FormatMessageA(dwFlags, lpSource, dwMessageId, dwLangId, + cpbuf, nSize, Arguments); + if (retval > 0) + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); + __PHYSFS_smallFree(cpbuf); + return(retval); +} /* fallbackFormatMessageW */ + +static DWORD WINAPI fallbackGetModuleFileNameW(HMODULE hMod, LPWCH lpBuf, + DWORD nSize) +{ + char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize); + DWORD retval = GetModuleFileNameA(hMod, cpbuf, nSize); + if (retval > 0) + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); + __PHYSFS_smallFree(cpbuf); + return(retval); +} /* fallbackGetModuleFileNameW */ + +static DWORD WINAPI fallbackGetFileAttributesW(LPCWSTR fname) +{ + DWORD retval = 0; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = GetFileAttributesA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackGetFileAttributesW */ + +static DWORD WINAPI fallbackGetCurrentDirectoryW(DWORD buflen, LPWSTR buf) +{ + DWORD retval = 0; + char *cpbuf = NULL; + if (buf != NULL) + cpbuf = (char *) __PHYSFS_smallAlloc(buflen); + retval = GetCurrentDirectoryA(buflen, cpbuf); + if (cpbuf != NULL) + { + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,buf,buflen); + __PHYSFS_smallFree(cpbuf); + } /* if */ + return(retval); +} /* fallbackGetCurrentDirectoryW */ + +static BOOL WINAPI fallbackRemoveDirectoryW(LPCWSTR dname) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(dname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); + retval = RemoveDirectoryA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackRemoveDirectoryW */ + +static BOOL WINAPI fallbackCreateDirectoryW(LPCWSTR dname, + LPSECURITY_ATTRIBUTES attr) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(dname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); + retval = CreateDirectoryA(cpstr, attr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackCreateDirectoryW */ + +static BOOL WINAPI fallbackDeleteFileW(LPCWSTR fname) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = DeleteFileA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackDeleteFileW */ + +static HANDLE WINAPI fallbackCreateFileW(LPCWSTR fname, + DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttrs, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttrs, HANDLE hTemplFile) +{ + HANDLE retval; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = CreateFileA(cpstr, dwDesiredAccess, dwShareMode, lpSecurityAttrs, + dwCreationDisposition, dwFlagsAndAttrs, hTemplFile); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackCreateFileW */ + + +#if (PHYSFS_MINIMUM_GCC_VERSION(3,3)) + typedef FARPROC __attribute__((__may_alias__)) PHYSFS_FARPROC; +#else + typedef FARPROC PHYSFS_FARPROC; +#endif + + +static void symLookup(HMODULE dll, PHYSFS_FARPROC *addr, const char *sym, + int reallyLook, PHYSFS_FARPROC fallback) +{ + PHYSFS_FARPROC proc; + proc = (PHYSFS_FARPROC) ((reallyLook) ? GetProcAddress(dll, sym) : NULL); + if (proc == NULL) + proc = fallback; /* may also be NULL. */ + *addr = proc; +} /* symLookup */ + + +static int findApiSymbols(void) +{ + HMODULE dll = NULL; + + #define LOOKUP_NOFALLBACK(x, reallyLook) \ + symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, reallyLook, NULL) + + #define LOOKUP(x, reallyLook) \ + symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, \ + reallyLook, (PHYSFS_FARPROC) fallback##x) + + /* Apparently Win9x HAS the Unicode entry points, they just don't WORK. */ + /* ...so don't look them up unless we're on NT+. (see osHasUnicode.) */ + + dll = libUserEnv = LoadLibraryA("userenv.dll"); + if (dll != NULL) + LOOKUP_NOFALLBACK(GetUserProfileDirectoryW, osHasUnicode); + + /* !!! FIXME: what do they call advapi32.dll on Win64? */ + dll = libAdvApi32 = LoadLibraryA("advapi32.dll"); + if (dll != NULL) + LOOKUP(GetUserNameW, osHasUnicode); + + /* !!! FIXME: what do they call kernel32.dll on Win64? */ + dll = libKernel32 = LoadLibraryA("kernel32.dll"); + if (dll != NULL) + { + LOOKUP_NOFALLBACK(GetFileAttributesExA, 1); + LOOKUP_NOFALLBACK(GetFileAttributesExW, osHasUnicode); + LOOKUP_NOFALLBACK(FindFirstFileW, osHasUnicode); + LOOKUP_NOFALLBACK(FindNextFileW, osHasUnicode); + LOOKUP(GetModuleFileNameW, osHasUnicode); + LOOKUP(FormatMessageW, osHasUnicode); + LOOKUP(GetFileAttributesW, osHasUnicode); + LOOKUP(GetCurrentDirectoryW, osHasUnicode); + LOOKUP(CreateDirectoryW, osHasUnicode); + LOOKUP(RemoveDirectoryW, osHasUnicode); + LOOKUP(CreateFileW, osHasUnicode); + LOOKUP(DeleteFileW, osHasUnicode); + } /* if */ + + #undef LOOKUP_NOFALLBACK + #undef LOOKUP + + return(1); +} /* findApiSymbols */ + + +const char *__PHYSFS_platformDirSeparator = "\\"; + + +/* + * Figure out what the last failing Windows API call was, and + * generate a human-readable string for the error message. + * + * The return value is a static buffer that is overwritten with + * each call to this function. + */ +static const char *winApiStrError(void) +{ + static char utf8buf[255]; + WCHAR msgbuf[255]; + WCHAR *ptr; + DWORD rc = pFormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, __PHYSFS_ARRAYLEN(msgbuf), + NULL); + + if (rc == 0) + msgbuf[0] = '\0'; /* oh well. */ + + /* chop off newlines. */ + for (ptr = msgbuf; *ptr; ptr++) + { + if ((*ptr == '\n') || (*ptr == '\r')) + { + *ptr = '\0'; + break; + } /* if */ + } /* for */ + + /* may truncate, but oh well. */ + PHYSFS_utf8FromUcs2((PHYSFS_uint16 *) msgbuf, utf8buf, sizeof (utf8buf)); + return((const char *) utf8buf); +} /* winApiStrError */ + + +static char *getExePath(void) +{ + DWORD buflen = 64; + LPWSTR modpath = NULL; + char *retval = NULL; + + while (1) + { + DWORD rc; + void *ptr; + + if ( !(ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) ) + { + allocator.Free(modpath); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + modpath = (LPWSTR) ptr; + + rc = pGetModuleFileNameW(NULL, modpath, buflen); + if (rc == 0) + { + allocator.Free(modpath); + BAIL_MACRO(winApiStrError(), NULL); + } /* if */ + + if (rc < buflen) + { + buflen = rc; + break; + } /* if */ + + buflen *= 2; + } /* while */ + + if (buflen > 0) /* just in case... */ + { + WCHAR *ptr = (modpath + buflen) - 1; + while (ptr != modpath) + { + if (*ptr == '\\') + break; + ptr--; + } /* while */ + + if ((ptr == modpath) && (*ptr != '\\')) + __PHYSFS_setError(ERR_GETMODFN_NO_DIR); + else + { + *(ptr + 1) = '\0'; /* chop off filename. */ + retval = unicodeToUtf8Heap(modpath); + } /* else */ + } /* else */ + allocator.Free(modpath); + + return(retval); /* w00t. */ +} /* getExePath */ + + +/* + * Try to make use of GetUserProfileDirectoryW(), which isn't available on + * some common variants of Win32. If we can't use this, we just punt and + * use the physfs base dir for the user dir, too. + * + * On success, module-scope variable (userDir) will have a pointer to + * a malloc()'d string of the user's profile dir, and a non-zero value is + * returned. If we can't determine the profile dir, (userDir) will + * be NULL, and zero is returned. + */ +static int determineUserDir(void) +{ + if (userDir != NULL) + return(1); /* already good to go. */ + + /* + * GetUserProfileDirectoryW() is only available on NT 4.0 and later. + * This means Win95/98/ME (and CE?) users have to do without, so for + * them, we'll default to the base directory when we can't get the + * function pointer. Since this is originally an NT API, we don't + * offer a non-Unicode fallback. + */ + if (pGetUserProfileDirectoryW != NULL) + { + HANDLE accessToken = NULL; /* Security handle to process */ + HANDLE processHandle = GetCurrentProcess(); + if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken)) + { + DWORD psize = 0; + WCHAR dummy = 0; + LPWSTR wstr = NULL; + BOOL rc = 0; + + /* + * Should fail. Will write the size of the profile path in + * psize. Also note that the second parameter can't be + * NULL or the function fails. + */ + rc = pGetUserProfileDirectoryW(accessToken, &dummy, &psize); + assert(!rc); /* !!! FIXME: handle this gracefully. */ + (void)rc; + + /* Allocate memory for the profile directory */ + wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR)); + if (wstr != NULL) + { + if (pGetUserProfileDirectoryW(accessToken, wstr, &psize)) + userDir = unicodeToUtf8Heap(wstr); + __PHYSFS_smallFree(wstr); + } /* else */ + } /* if */ + + CloseHandle(accessToken); + } /* if */ + + if (userDir == NULL) /* couldn't get profile for some reason. */ + { + /* Might just be a non-NT system; resort to the basedir. */ + userDir = getExePath(); + BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */ + } /* if */ + + return(1); /* We made it: hit the showers. */ +} /* determineUserDir */ + + +static BOOL mediaInDrive(const char *drive) +{ + UINT oldErrorMode; + DWORD tmp; + BOOL retval; + + /* Prevent windows warning message appearing when checking media size */ + oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); + + /* If this function succeeds, there's media in the drive */ + retval = GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0); + + /* Revert back to old windows error handler */ + SetErrorMode(oldErrorMode); + + return(retval); +} /* mediaInDrive */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + /* !!! FIXME: Can CD drives be non-drive letter paths? */ + /* !!! FIXME: (so can they be Unicode paths?) */ + char drive_str[4] = "x:\\"; + char ch; + for (ch = 'A'; ch <= 'Z'; ch++) + { + drive_str[0] = ch; + if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str)) + cb(data, drive_str); + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + if ((argv0 != NULL) && (strchr(argv0, '\\') != NULL)) + return(NULL); /* default behaviour can handle this. */ + + return(getExePath()); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + DWORD bufsize = 0; + char *retval = NULL; + + if (pGetUserNameW(NULL, &bufsize) == 0) /* This SHOULD fail. */ + { + LPWSTR wbuf = (LPWSTR) __PHYSFS_smallAlloc(bufsize * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + if (pGetUserNameW(wbuf, &bufsize) == 0) /* ?! */ + __PHYSFS_setError(winApiStrError()); + else + retval = unicodeToUtf8Heap(wbuf); + __PHYSFS_smallFree(wbuf); + } /* if */ + + return(retval); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + char *retval = (char *) allocator.Malloc(strlen(userDir) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, userDir); /* calculated at init time. */ + return(retval); +} /* __PHYSFS_platformGetUserDir */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + return( (void *) ((size_t) GetCurrentThreadId()) ); +} /* __PHYSFS_platformGetThreadID */ + + +static int doPlatformExists(LPWSTR wpath) +{ + BAIL_IF_MACRO + ( + pGetFileAttributesW(wpath) == PHYSFS_INVALID_FILE_ATTRIBUTES, + winApiStrError(), 0 + ); + return(1); +} /* doPlatformExists */ + + +int __PHYSFS_platformExists(const char *fname) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doPlatformExists(wpath); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformExists */ + + +static int isSymlinkAttrs(const DWORD attr, const DWORD tag) +{ + return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) && + (tag == PHYSFS_IO_REPARSE_TAG_SYMLINK) ); +} /* isSymlinkAttrs */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + /* !!! FIXME: + * Windows Vista can have NTFS symlinks. Can older Windows releases have + * them when talking to a network file server? What happens when you + * mount a NTFS partition on XP that was plugged into a Vista install + * that made a symlink? + */ + + int retval = 0; + LPWSTR wpath; + HANDLE dir; + WIN32_FIND_DATAW entw; + + /* no unicode entry points? Probably no symlinks. */ + BAIL_IF_MACRO(pFindFirstFileW == NULL, NULL, 0); + + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + + /* !!! FIXME: filter wildcard chars? */ + dir = pFindFirstFileW(wpath, &entw); + if (dir != INVALID_HANDLE_VALUE) + { + retval = isSymlinkAttrs(entw.dwFileAttributes, entw.dwReserved0); + FindClose(dir); + } /* if */ + + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = ((pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) != 0); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL); + HANDLE dir = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ent; + WIN32_FIND_DATAW entw; + size_t len = strlen(dirname); + char *searchPath = NULL; + WCHAR *wSearchPath = NULL; + char *utf8 = NULL; + + /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ + searchPath = (char *) __PHYSFS_smallAlloc(len + 3); + if (searchPath == NULL) + return; + + /* Copy current dirname */ + strcpy(searchPath, dirname); + + /* if there's no '\\' at the end of the path, stick one in there. */ + if (searchPath[len - 1] != '\\') + { + searchPath[len++] = '\\'; + searchPath[len] = '\0'; + } /* if */ + + /* Append the "*" to the end of the string */ + strcat(searchPath, "*"); + + UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath); + if (wSearchPath == NULL) + return; /* oh well. */ + + if (unicode) + dir = pFindFirstFileW(wSearchPath, &entw); + else + { + const int len = (int) (wStrLen(wSearchPath) + 1); + char *cp = (char *) __PHYSFS_smallAlloc(len); + if (cp != NULL) + { + WideCharToMultiByte(CP_ACP, 0, wSearchPath, len, cp, len, 0, 0); + dir = FindFirstFileA(cp, &ent); + __PHYSFS_smallFree(cp); + } /* if */ + } /* else */ + + __PHYSFS_smallFree(wSearchPath); + __PHYSFS_smallFree(searchPath); + if (dir == INVALID_HANDLE_VALUE) + return; + + if (unicode) + { + do + { + const DWORD attr = entw.dwFileAttributes; + const DWORD tag = entw.dwReserved0; + const WCHAR *fn = entw.cFileName; + if ((fn[0] == '.') && (fn[1] == '\0')) + continue; + if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) + continue; + if ((omitSymLinks) && (isSymlinkAttrs(attr, tag))) + continue; + + utf8 = unicodeToUtf8Heap(fn); + if (utf8 != NULL) + { + callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + } /* if */ + } while (pFindNextFileW(dir, &entw) != 0); + } /* if */ + + else /* ANSI fallback. */ + { + do + { + const DWORD attr = ent.dwFileAttributes; + const DWORD tag = ent.dwReserved0; + const char *fn = ent.cFileName; + if ((fn[0] == '.') && (fn[1] == '\0')) + continue; + if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) + continue; + if ((omitSymLinks) && (isSymlinkAttrs(attr, tag))) + continue; + + utf8 = codepageToUtf8Heap(fn); + if (utf8 != NULL) + { + callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + } /* if */ + } while (FindNextFileA(dir, &ent) != 0); + } /* else */ + + FindClose(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + char *retval = NULL; + WCHAR *wbuf = NULL; + DWORD buflen = 0; + + buflen = pGetCurrentDirectoryW(buflen, NULL); + wbuf = (WCHAR *) __PHYSFS_smallAlloc((buflen + 2) * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + pGetCurrentDirectoryW(buflen, wbuf); + + if (wbuf[buflen - 2] == '\\') + wbuf[buflen-1] = '\0'; /* just in case... */ + else + { + wbuf[buflen - 1] = '\\'; + wbuf[buflen] = '\0'; + } /* else */ + + retval = unicodeToUtf8Heap(wbuf); + __PHYSFS_smallFree(wbuf); + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +/* this could probably use a cleanup. */ +char *__PHYSFS_platformRealPath(const char *path) +{ + /* !!! FIXME: this should return NULL if (path) doesn't exist? */ + /* !!! FIXME: Need to handle symlinks in Vista... */ + /* !!! FIXME: try GetFullPathName() instead? */ + /* this function should be UTF-8 clean. */ + char *retval = NULL; + char *p = NULL; + + BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(*path == '\0', ERR_INVALID_ARGUMENT, NULL); + + retval = (char *) allocator.Malloc(MAX_PATH); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* + * If in \\server\path format, it's already an absolute path. + * We'll need to check for "." and ".." dirs, though, just in case. + */ + if ((path[0] == '\\') && (path[1] == '\\')) + strcpy(retval, path); + + else + { + char *currentDir = __PHYSFS_platformCurrentDir(); + if (currentDir == NULL) + { + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + if (path[1] == ':') /* drive letter specified? */ + { + /* + * Apparently, "D:mypath" is the same as "D:\\mypath" if + * D: is not the current drive. However, if D: is the + * current drive, then "D:mypath" is a relative path. Ugh. + */ + if (path[2] == '\\') /* maybe an absolute path? */ + strcpy(retval, path); + else /* definitely an absolute path. */ + { + if (path[0] == currentDir[0]) /* current drive; relative. */ + { + strcpy(retval, currentDir); + strcat(retval, path + 2); + } /* if */ + + else /* not current drive; absolute. */ + { + retval[0] = path[0]; + retval[1] = ':'; + retval[2] = '\\'; + strcpy(retval + 3, path + 2); + } /* else */ + } /* else */ + } /* if */ + + else /* no drive letter specified. */ + { + if (path[0] == '\\') /* absolute path. */ + { + retval[0] = currentDir[0]; + retval[1] = ':'; + strcpy(retval + 2, path); + } /* if */ + else + { + strcpy(retval, currentDir); + strcat(retval, path); + } /* else */ + } /* else */ + + allocator.Free(currentDir); + } /* else */ + + /* (whew.) Ok, now take out "." and ".." path entries... */ + + p = retval; + while ( (p = strstr(p, "\\.")) != NULL) + { + /* it's a "." entry that doesn't end the string. */ + if (p[2] == '\\') + memmove(p + 1, p + 3, strlen(p + 3) + 1); + + /* it's a "." entry that ends the string. */ + else if (p[2] == '\0') + p[0] = '\0'; + + /* it's a ".." entry. */ + else if (p[2] == '.') + { + char *prevEntry = p - 1; + while ((prevEntry != retval) && (*prevEntry != '\\')) + prevEntry--; + + if (prevEntry == retval) /* make it look like a "." entry. */ + memmove(p + 1, p + 2, strlen(p + 2) + 1); + else + { + if (p[3] != '\0') /* doesn't end string. */ + *prevEntry = '\0'; + else /* ends string. */ + memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1); + + p = prevEntry; + } /* else */ + } /* else if */ + + else + { + p++; /* look past current char. */ + } /* else */ + } /* while */ + + /* shrink the retval's memory block if possible... */ + p = (char *) allocator.Realloc(retval, strlen(retval) + 1); + if (p != NULL) + retval = p; + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + WCHAR *wpath; + DWORD rc; + UTF8_TO_UNICODE_STACK_MACRO(wpath, path); + rc = pCreateDirectoryW(wpath, NULL); + __PHYSFS_smallFree(wpath); + BAIL_IF_MACRO(rc == 0, winApiStrError(), 0); + return(1); +} /* __PHYSFS_platformMkDir */ + + + /* + * Get OS info and save the important parts. + * + * Returns non-zero if successful, otherwise it returns zero on failure. + */ +static int getOSInfo(void) +{ + OSVERSIONINFO osVerInfo; /* Information about the OS */ + osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo); + BAIL_IF_MACRO(!GetVersionEx(&osVerInfo), winApiStrError(), 0); + osHasUnicode = (osVerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS); + return(1); +} /* getOSInfo */ + + +int __PHYSFS_platformInit(void) +{ + BAIL_IF_MACRO(!getOSInfo(), NULL, 0); + BAIL_IF_MACRO(!findApiSymbols(), NULL, 0); + BAIL_IF_MACRO(!determineUserDir(), NULL, 0); + return(1); /* It's all good */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL }; + int i; + + allocator.Free(userDir); + userDir = NULL; + + for (i = 0; libs[i] != NULL; i++) + { + const HANDLE lib = *(libs[i]); + if (lib) + FreeLibrary(lib); + *(libs[i]) = NULL; + } /* for */ + + return(1); /* It's all good */ +} /* __PHYSFS_platformDeinit */ + + +static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly) +{ + HANDLE fileHandle; + WinApiFile *retval; + WCHAR *wfname; + + UTF8_TO_UNICODE_STACK_MACRO(wfname, fname); + BAIL_IF_MACRO(wfname == NULL, ERR_OUT_OF_MEMORY, NULL); + fileHandle = pCreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + __PHYSFS_smallFree(wfname); + + BAIL_IF_MACRO + ( + fileHandle == INVALID_HANDLE_VALUE, + winApiStrError(), NULL + ); + + retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile)); + if (retval == NULL) + { + CloseHandle(fileHandle); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval->readonly = rdonly; + retval->handle = fileHandle; + return(retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1)); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0)); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0); + if (retval != NULL) + { + HANDLE h = ((WinApiFile *) retval)->handle; + DWORD rc = SetFilePointer(h, 0, NULL, FILE_END); + if (rc == PHYSFS_INVALID_SET_FILE_POINTER) + { + const char *err = winApiStrError(); + CloseHandle(h); + allocator.Free(retval); + BAIL_MACRO(err, NULL); + } /* if */ + } /* if */ + + return(retval); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD CountOfBytesRead; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /* !!! FIXME: uint32 might be a greater # than DWORD */ + if(!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL)) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! FIXME: What if not the right amount of bytes was read to make an object? */ + retval = CountOfBytesRead / size; + } /* else */ + + return(retval); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD CountOfBytesWritten; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /* !!! FIXME: uint32 might be a greater # than DWORD */ + if(!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL)) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! FIXME: What if not the right number of bytes was written? */ + retval = CountOfBytesWritten / size; + } /* else */ + + return(retval); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + LONG HighOrderPos; + PLONG pHighOrderPos; + DWORD rc; + + /* Get the high order 32-bits of the position */ + HighOrderPos = HIGHORDER_UINT64(pos); + + /* + * MSDN: "If you do not need the high-order 32 bits, this + * pointer must be set to NULL." + */ + pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL; + + /* + * !!! FIXME: MSDN: "Windows Me/98/95: If the pointer + * !!! FIXME: lpDistanceToMoveHigh is not NULL, then it must + * !!! FIXME: point to either 0, INVALID_SET_FILE_POINTER, or + * !!! FIXME: the sign extension of the value of lDistanceToMove. + * !!! FIXME: Any other value will be rejected." + */ + + /* Move pointer "pos" count from start of file */ + rc = SetFilePointer(Handle, LOWORDER_UINT64(pos), + pHighOrderPos, FILE_BEGIN); + + if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), 0); + } /* if */ + + return(1); /* No error occured */ +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + LONG HighPos = 0; + DWORD LowPos; + PHYSFS_sint64 retval; + + /* Get current position */ + LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT); + if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos; + assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD SizeHigh; + DWORD SizeLow; + PHYSFS_sint64 retval; + + SizeLow = GetFileSize(Handle, &SizeHigh); + if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow; + assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + const PHYSFS_sint64 FileLength = __PHYSFS_platformFileLength(opaque); + PHYSFS_sint64 FilePosition; + int retval = 0; + + if (FileLength == 0) + return 1; /* we're definitely at EOF. */ + + /* Get the current position in the file */ + if ((FilePosition = __PHYSFS_platformTell(opaque)) != -1) + { + /* Non-zero if EOF is equal to the file length */ + retval = (FilePosition == FileLength); + } /* if */ + + return(retval); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + WinApiFile *fh = ((WinApiFile *) opaque); + if (!fh->readonly) + BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), winApiStrError(), 0); + + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + BAIL_IF_MACRO(!CloseHandle(Handle), winApiStrError(), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +static int doPlatformDelete(LPWSTR wpath) +{ + /* If filename is a folder */ + if (pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) + { + BAIL_IF_MACRO(!pRemoveDirectoryW(wpath), winApiStrError(), 0); + } /* if */ + else + { + BAIL_IF_MACRO(!pDeleteFileW(wpath), winApiStrError(), 0); + } /* else */ + + return(1); /* if you made it here, it worked. */ +} /* doPlatformDelete */ + + +int __PHYSFS_platformDelete(const char *path) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, path); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doPlatformDelete(wpath); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformDelete */ + + +/* + * !!! FIXME: why aren't we using Critical Sections instead of Mutexes? + * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are + * !!! FIXME: mutexes for threads in a single process and are faster. + */ +void *__PHYSFS_platformCreateMutex(void) +{ + return((void *) CreateMutex(NULL, FALSE, NULL)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + CloseHandle((HANDLE) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ReleaseMutex((HANDLE) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft) +{ + SYSTEMTIME st_utc; + SYSTEMTIME st_localtz; + TIME_ZONE_INFORMATION tzi; + DWORD tzid; + PHYSFS_sint64 retval; + struct tm tm; + + BAIL_IF_MACRO(!FileTimeToSystemTime(ft, &st_utc), winApiStrError(), -1); + tzid = GetTimeZoneInformation(&tzi); + BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, winApiStrError(), -1); + + /* (This API is unsupported and fails on non-NT systems. */ + if (!SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz)) + { + /* do it by hand. Grumble... */ + ULARGE_INTEGER ui64; + FILETIME new_ft; + ui64.LowPart = ft->dwLowDateTime; + ui64.HighPart = ft->dwHighDateTime; + + if (tzid == TIME_ZONE_ID_STANDARD) + tzi.Bias += tzi.StandardBias; + else if (tzid == TIME_ZONE_ID_DAYLIGHT) + tzi.Bias += tzi.DaylightBias; + + /* convert from minutes to 100-nanosecond increments... */ + ui64.QuadPart -= (((LONGLONG) tzi.Bias) * (600000000)); + + /* Move it back into a FILETIME structure... */ + new_ft.dwLowDateTime = ui64.LowPart; + new_ft.dwHighDateTime = ui64.HighPart; + + /* Convert to something human-readable... */ + if (!FileTimeToSystemTime(&new_ft, &st_localtz)) + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = st_localtz.wSecond; + tm.tm_min = st_localtz.wMinute; + tm.tm_hour = st_localtz.wHour; + tm.tm_mday = st_localtz.wDay; + tm.tm_mon = st_localtz.wMonth - 1; + tm.tm_year = st_localtz.wYear - 1900; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + return(retval); +} /* FileTimeToPhysfsTime */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + PHYSFS_sint64 retval = -1; + WIN32_FILE_ATTRIBUTE_DATA attr; + int rc = 0; + + memset(&attr, '\0', sizeof (attr)); + + /* GetFileAttributesEx didn't show up until Win98 and NT4. */ + if ((pGetFileAttributesExW != NULL) || (pGetFileAttributesExA != NULL)) + { + WCHAR *wstr; + UTF8_TO_UNICODE_STACK_MACRO(wstr, fname); + if (wstr != NULL) /* if NULL, maybe the fallback will work. */ + { + if (pGetFileAttributesExW != NULL) /* NT/XP/Vista/etc system. */ + rc = pGetFileAttributesExW(wstr, GetFileExInfoStandard, &attr); + else /* Win98/ME system */ + { + const int len = (int) (wStrLen(wstr) + 1); + char *cp = (char *) __PHYSFS_smallAlloc(len); + if (cp != NULL) + { + WideCharToMultiByte(CP_ACP, 0, wstr, len, cp, len, 0, 0); + rc = pGetFileAttributesExA(cp, GetFileExInfoStandard, &attr); + __PHYSFS_smallFree(cp); + } /* if */ + } /* else */ + __PHYSFS_smallFree(wstr); + } /* if */ + } /* if */ + + if (rc) /* had API entry point and it worked. */ + { + /* 0 return value indicates an error or not supported */ + if ( (attr.ftLastWriteTime.dwHighDateTime != 0) || + (attr.ftLastWriteTime.dwLowDateTime != 0) ) + { + retval = FileTimeToPhysfsTime(&attr.ftLastWriteTime); + } /* if */ + } /* if */ + + /* GetFileTime() has been in the Win32 API since the start. */ + if (retval == -1) /* try a fallback... */ + { + FILETIME ft; + BOOL rc; + const char *err; + WinApiFile *f = (WinApiFile *) __PHYSFS_platformOpenRead(fname); + BAIL_IF_MACRO(f == NULL, NULL, -1) + rc = GetFileTime(f->handle, NULL, NULL, &ft); + err = winApiStrError(); + CloseHandle(f->handle); + allocator.Free(f); + BAIL_IF_MACRO(!rc, err, -1); + retval = FileTimeToPhysfsTime(&ft); + } /* if */ + + return(retval); +} /* __PHYSFS_platformGetLastModTime */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_WINDOWS */ + +/* end of windows.c ... */ + + diff --git a/3rdparty/physfs/zlib123/README b/3rdparty/physfs/zlib123/README new file mode 100644 index 0000000..758cc50 --- /dev/null +++ b/3rdparty/physfs/zlib123/README @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/3rdparty/physfs/zlib123/adler32.c b/3rdparty/physfs/zlib123/adler32.c new file mode 100644 index 0000000..007ba26 --- /dev/null +++ b/3rdparty/physfs/zlib123/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/3rdparty/physfs/zlib123/compress.c b/3rdparty/physfs/zlib123/compress.c new file mode 100644 index 0000000..df04f01 --- /dev/null +++ b/3rdparty/physfs/zlib123/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/3rdparty/physfs/zlib123/crc32.c b/3rdparty/physfs/zlib123/crc32.c new file mode 100644 index 0000000..f658a9e --- /dev/null +++ b/3rdparty/physfs/zlib123/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/3rdparty/physfs/zlib123/crc32.h b/3rdparty/physfs/zlib123/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/3rdparty/physfs/zlib123/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/3rdparty/physfs/zlib123/deflate.c b/3rdparty/physfs/zlib123/deflate.c new file mode 100644 index 0000000..29ce1f6 --- /dev/null +++ b/3rdparty/physfs/zlib123/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/3rdparty/physfs/zlib123/deflate.h b/3rdparty/physfs/zlib123/deflate.h new file mode 100644 index 0000000..05a5ab3 --- /dev/null +++ b/3rdparty/physfs/zlib123/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/3rdparty/physfs/zlib123/gzio.c b/3rdparty/physfs/zlib123/gzio.c new file mode 100644 index 0000000..7e90f49 --- /dev/null +++ b/3rdparty/physfs/zlib123/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/3rdparty/physfs/zlib123/infback.c b/3rdparty/physfs/zlib123/infback.c new file mode 100644 index 0000000..455dbc9 --- /dev/null +++ b/3rdparty/physfs/zlib123/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/3rdparty/physfs/zlib123/inffast.c b/3rdparty/physfs/zlib123/inffast.c new file mode 100644 index 0000000..bbee92e --- /dev/null +++ b/3rdparty/physfs/zlib123/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/3rdparty/physfs/zlib123/inffast.h b/3rdparty/physfs/zlib123/inffast.h new file mode 100644 index 0000000..1e88d2d --- /dev/null +++ b/3rdparty/physfs/zlib123/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/3rdparty/physfs/zlib123/inffixed.h b/3rdparty/physfs/zlib123/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/3rdparty/physfs/zlib123/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/3rdparty/physfs/zlib123/inflate.c b/3rdparty/physfs/zlib123/inflate.c new file mode 100644 index 0000000..792fdee --- /dev/null +++ b/3rdparty/physfs/zlib123/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/3rdparty/physfs/zlib123/inflate.h b/3rdparty/physfs/zlib123/inflate.h new file mode 100644 index 0000000..07bd3e7 --- /dev/null +++ b/3rdparty/physfs/zlib123/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/3rdparty/physfs/zlib123/inftrees.c b/3rdparty/physfs/zlib123/inftrees.c new file mode 100644 index 0000000..8a9c13f --- /dev/null +++ b/3rdparty/physfs/zlib123/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/3rdparty/physfs/zlib123/inftrees.h b/3rdparty/physfs/zlib123/inftrees.h new file mode 100644 index 0000000..b1104c8 --- /dev/null +++ b/3rdparty/physfs/zlib123/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/3rdparty/physfs/zlib123/trees.c b/3rdparty/physfs/zlib123/trees.c new file mode 100644 index 0000000..395e4e1 --- /dev/null +++ b/3rdparty/physfs/zlib123/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/3rdparty/physfs/zlib123/trees.h b/3rdparty/physfs/zlib123/trees.h new file mode 100644 index 0000000..72facf9 --- /dev/null +++ b/3rdparty/physfs/zlib123/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/3rdparty/physfs/zlib123/uncompr.c b/3rdparty/physfs/zlib123/uncompr.c new file mode 100644 index 0000000..b59e3d0 --- /dev/null +++ b/3rdparty/physfs/zlib123/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/3rdparty/physfs/zlib123/zconf.h b/3rdparty/physfs/zlib123/zconf.h new file mode 100644 index 0000000..03a9431 --- /dev/null +++ b/3rdparty/physfs/zlib123/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/3rdparty/physfs/zlib123/zlib.h b/3rdparty/physfs/zlib123/zlib.h new file mode 100644 index 0000000..0228179 --- /dev/null +++ b/3rdparty/physfs/zlib123/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/3rdparty/physfs/zlib123/zutil.c b/3rdparty/physfs/zlib123/zutil.c new file mode 100644 index 0000000..d55f594 --- /dev/null +++ b/3rdparty/physfs/zlib123/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/3rdparty/physfs/zlib123/zutil.h b/3rdparty/physfs/zlib123/zutil.h new file mode 100644 index 0000000..b7d5eff --- /dev/null +++ b/3rdparty/physfs/zlib123/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/3rdparty/thirdpartysoftware.txt b/3rdparty/thirdpartysoftware.txt new file mode 100644 index 0000000..6485f5b --- /dev/null +++ b/3rdparty/thirdpartysoftware.txt @@ -0,0 +1,10 @@ +Note: This list doesnt include pulled in dependencies by git + +Graphite includes these third party software in-tree (in no particular order): +- Dear ImGui (https://github.com/ocornut/imgui/) +- glsl-atmosphere (https://github.com/wwwtyro/glsl-atmosphere/) +- ToolWindowManager (https://github.com/Riateche/toolwindowmanager/) +- glad (https://github.com/Dav1dde/glad) +- angelscript (http://www.angelcode.com/angelscript) + +If I forget to add anything to this list, please contact me on github \ No newline at end of file diff --git a/3rdparty/vulkan/include/vulkan/vk_icd.h b/3rdparty/vulkan/include/vulkan/vk_icd.h new file mode 100644 index 0000000..7b54fb5 --- /dev/null +++ b/3rdparty/vulkan/include/vulkan/vk_icd.h @@ -0,0 +1,131 @@ +// +// File: vk_icd.h +// +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef VKICD_H +#define VKICD_H + +#include "vulkan.h" + +/* + * Loader-ICD version negotiation API + */ +#define CURRENT_LOADER_ICD_INTERFACE_VERSION 3 +#define MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION 0 +typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderICDInterfaceVersion)(uint32_t *pVersion); +/* + * The ICD must reserve space for a pointer for the loader's dispatch + * table, at the start of . + * The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro. + */ + +#define ICD_LOADER_MAGIC 0x01CDC0DE + +typedef union { + uintptr_t loaderMagic; + void *loaderData; +} VK_LOADER_DATA; + +static inline void set_loader_magic_value(void *pNewObject) { + VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; + loader_info->loaderMagic = ICD_LOADER_MAGIC; +} + +static inline bool valid_loader_magic_value(void *pNewObject) { + const VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; + return (loader_info->loaderMagic & 0xffffffff) == ICD_LOADER_MAGIC; +} + +/* + * Windows and Linux ICDs will treat VkSurfaceKHR as a pointer to a struct that + * contains the platform-specific connection and surface information. + */ +typedef enum { + VK_ICD_WSI_PLATFORM_MIR, + VK_ICD_WSI_PLATFORM_WAYLAND, + VK_ICD_WSI_PLATFORM_WIN32, + VK_ICD_WSI_PLATFORM_XCB, + VK_ICD_WSI_PLATFORM_XLIB, + VK_ICD_WSI_PLATFORM_DISPLAY +} VkIcdWsiPlatform; + +typedef struct { + VkIcdWsiPlatform platform; +} VkIcdSurfaceBase; + +#ifdef VK_USE_PLATFORM_MIR_KHR +typedef struct { + VkIcdSurfaceBase base; + MirConnection *connection; + MirSurface *mirSurface; +} VkIcdSurfaceMir; +#endif // VK_USE_PLATFORM_MIR_KHR + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +typedef struct { + VkIcdSurfaceBase base; + struct wl_display *display; + struct wl_surface *surface; +} VkIcdSurfaceWayland; +#endif // VK_USE_PLATFORM_WAYLAND_KHR + +#ifdef VK_USE_PLATFORM_WIN32_KHR +typedef struct { + VkIcdSurfaceBase base; + HINSTANCE hinstance; + HWND hwnd; +} VkIcdSurfaceWin32; +#endif // VK_USE_PLATFORM_WIN32_KHR + +#ifdef VK_USE_PLATFORM_XCB_KHR +typedef struct { + VkIcdSurfaceBase base; + xcb_connection_t *connection; + xcb_window_t window; +} VkIcdSurfaceXcb; +#endif // VK_USE_PLATFORM_XCB_KHR + +#ifdef VK_USE_PLATFORM_XLIB_KHR +typedef struct { + VkIcdSurfaceBase base; + Display *dpy; + Window window; +} VkIcdSurfaceXlib; +#endif // VK_USE_PLATFORM_XLIB_KHR + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +typedef struct { + ANativeWindow* window; +} VkIcdSurfaceAndroid; +#endif //VK_USE_PLATFORM_ANDROID_KHR + +typedef struct { + VkIcdSurfaceBase base; + VkDisplayModeKHR displayMode; + uint32_t planeIndex; + uint32_t planeStackIndex; + VkSurfaceTransformFlagBitsKHR transform; + float globalAlpha; + VkDisplayPlaneAlphaFlagBitsKHR alphaMode; + VkExtent2D imageExtent; +} VkIcdSurfaceDisplay; + +#endif // VKICD_H diff --git a/3rdparty/vulkan/include/vulkan/vk_layer.h b/3rdparty/vulkan/include/vulkan/vk_layer.h new file mode 100644 index 0000000..4d0da1a --- /dev/null +++ b/3rdparty/vulkan/include/vulkan/vk_layer.h @@ -0,0 +1,320 @@ +// +// File: vk_layer.h +// +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Need to define dispatch table + * Core struct can then have ptr to dispatch table at the top + * Along with object ptrs for current and next OBJ + */ +#pragma once + +#include "vulkan.h" +#if defined(__GNUC__) && __GNUC__ >= 4 +#define VK_LAYER_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define VK_LAYER_EXPORT __attribute__((visibility("default"))) +#else +#define VK_LAYER_EXPORT +#endif + +typedef struct VkLayerDispatchTable_ { + PFN_vkGetDeviceProcAddr GetDeviceProcAddr; + PFN_vkDestroyDevice DestroyDevice; + PFN_vkGetDeviceQueue GetDeviceQueue; + PFN_vkQueueSubmit QueueSubmit; + PFN_vkQueueWaitIdle QueueWaitIdle; + PFN_vkDeviceWaitIdle DeviceWaitIdle; + PFN_vkAllocateMemory AllocateMemory; + PFN_vkFreeMemory FreeMemory; + PFN_vkMapMemory MapMemory; + PFN_vkUnmapMemory UnmapMemory; + PFN_vkFlushMappedMemoryRanges FlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges InvalidateMappedMemoryRanges; + PFN_vkGetDeviceMemoryCommitment GetDeviceMemoryCommitment; + PFN_vkGetImageSparseMemoryRequirements GetImageSparseMemoryRequirements; + PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements; + PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements; + PFN_vkBindImageMemory BindImageMemory; + PFN_vkBindBufferMemory BindBufferMemory; + PFN_vkQueueBindSparse QueueBindSparse; + PFN_vkCreateFence CreateFence; + PFN_vkDestroyFence DestroyFence; + PFN_vkGetFenceStatus GetFenceStatus; + PFN_vkResetFences ResetFences; + PFN_vkWaitForFences WaitForFences; + PFN_vkCreateSemaphore CreateSemaphore; + PFN_vkDestroySemaphore DestroySemaphore; + PFN_vkCreateEvent CreateEvent; + PFN_vkDestroyEvent DestroyEvent; + PFN_vkGetEventStatus GetEventStatus; + PFN_vkSetEvent SetEvent; + PFN_vkResetEvent ResetEvent; + PFN_vkCreateQueryPool CreateQueryPool; + PFN_vkDestroyQueryPool DestroyQueryPool; + PFN_vkGetQueryPoolResults GetQueryPoolResults; + PFN_vkCreateBuffer CreateBuffer; + PFN_vkDestroyBuffer DestroyBuffer; + PFN_vkCreateBufferView CreateBufferView; + PFN_vkDestroyBufferView DestroyBufferView; + PFN_vkCreateImage CreateImage; + PFN_vkDestroyImage DestroyImage; + PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout; + PFN_vkCreateImageView CreateImageView; + PFN_vkDestroyImageView DestroyImageView; + PFN_vkCreateShaderModule CreateShaderModule; + PFN_vkDestroyShaderModule DestroyShaderModule; + PFN_vkCreatePipelineCache CreatePipelineCache; + PFN_vkDestroyPipelineCache DestroyPipelineCache; + PFN_vkGetPipelineCacheData GetPipelineCacheData; + PFN_vkMergePipelineCaches MergePipelineCaches; + PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines; + PFN_vkCreateComputePipelines CreateComputePipelines; + PFN_vkDestroyPipeline DestroyPipeline; + PFN_vkCreatePipelineLayout CreatePipelineLayout; + PFN_vkDestroyPipelineLayout DestroyPipelineLayout; + PFN_vkCreateSampler CreateSampler; + PFN_vkDestroySampler DestroySampler; + PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout; + PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout; + PFN_vkCreateDescriptorPool CreateDescriptorPool; + PFN_vkDestroyDescriptorPool DestroyDescriptorPool; + PFN_vkResetDescriptorPool ResetDescriptorPool; + PFN_vkAllocateDescriptorSets AllocateDescriptorSets; + PFN_vkFreeDescriptorSets FreeDescriptorSets; + PFN_vkUpdateDescriptorSets UpdateDescriptorSets; + PFN_vkCreateFramebuffer CreateFramebuffer; + PFN_vkDestroyFramebuffer DestroyFramebuffer; + PFN_vkCreateRenderPass CreateRenderPass; + PFN_vkDestroyRenderPass DestroyRenderPass; + PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity; + PFN_vkCreateCommandPool CreateCommandPool; + PFN_vkDestroyCommandPool DestroyCommandPool; + PFN_vkResetCommandPool ResetCommandPool; + PFN_vkAllocateCommandBuffers AllocateCommandBuffers; + PFN_vkFreeCommandBuffers FreeCommandBuffers; + PFN_vkBeginCommandBuffer BeginCommandBuffer; + PFN_vkEndCommandBuffer EndCommandBuffer; + PFN_vkResetCommandBuffer ResetCommandBuffer; + PFN_vkCmdBindPipeline CmdBindPipeline; + PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; + PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers; + PFN_vkCmdBindIndexBuffer CmdBindIndexBuffer; + PFN_vkCmdSetViewport CmdSetViewport; + PFN_vkCmdSetScissor CmdSetScissor; + PFN_vkCmdSetLineWidth CmdSetLineWidth; + PFN_vkCmdSetDepthBias CmdSetDepthBias; + PFN_vkCmdSetBlendConstants CmdSetBlendConstants; + PFN_vkCmdSetDepthBounds CmdSetDepthBounds; + PFN_vkCmdSetStencilCompareMask CmdSetStencilCompareMask; + PFN_vkCmdSetStencilWriteMask CmdSetStencilWriteMask; + PFN_vkCmdSetStencilReference CmdSetStencilReference; + PFN_vkCmdDraw CmdDraw; + PFN_vkCmdDrawIndexed CmdDrawIndexed; + PFN_vkCmdDrawIndirect CmdDrawIndirect; + PFN_vkCmdDrawIndexedIndirect CmdDrawIndexedIndirect; + PFN_vkCmdDispatch CmdDispatch; + PFN_vkCmdDispatchIndirect CmdDispatchIndirect; + PFN_vkCmdCopyBuffer CmdCopyBuffer; + PFN_vkCmdCopyImage CmdCopyImage; + PFN_vkCmdBlitImage CmdBlitImage; + PFN_vkCmdCopyBufferToImage CmdCopyBufferToImage; + PFN_vkCmdCopyImageToBuffer CmdCopyImageToBuffer; + PFN_vkCmdUpdateBuffer CmdUpdateBuffer; + PFN_vkCmdFillBuffer CmdFillBuffer; + PFN_vkCmdClearColorImage CmdClearColorImage; + PFN_vkCmdClearDepthStencilImage CmdClearDepthStencilImage; + PFN_vkCmdClearAttachments CmdClearAttachments; + PFN_vkCmdResolveImage CmdResolveImage; + PFN_vkCmdSetEvent CmdSetEvent; + PFN_vkCmdResetEvent CmdResetEvent; + PFN_vkCmdWaitEvents CmdWaitEvents; + PFN_vkCmdPipelineBarrier CmdPipelineBarrier; + PFN_vkCmdBeginQuery CmdBeginQuery; + PFN_vkCmdEndQuery CmdEndQuery; + PFN_vkCmdResetQueryPool CmdResetQueryPool; + PFN_vkCmdWriteTimestamp CmdWriteTimestamp; + PFN_vkCmdCopyQueryPoolResults CmdCopyQueryPoolResults; + PFN_vkCmdPushConstants CmdPushConstants; + PFN_vkCmdBeginRenderPass CmdBeginRenderPass; + PFN_vkCmdNextSubpass CmdNextSubpass; + PFN_vkCmdEndRenderPass CmdEndRenderPass; + PFN_vkCmdExecuteCommands CmdExecuteCommands; + PFN_vkCreateSwapchainKHR CreateSwapchainKHR; + PFN_vkDestroySwapchainKHR DestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR AcquireNextImageKHR; + PFN_vkQueuePresentKHR QueuePresentKHR; + PFN_vkCmdDrawIndirectCountAMD CmdDrawIndirectCountAMD; + PFN_vkCmdDrawIndexedIndirectCountAMD CmdDrawIndexedIndirectCountAMD; +#ifdef VK_USE_PLATFORM_WIN32_KHR + PFN_vkGetMemoryWin32HandleNV GetMemoryWin32HandleNV; +#endif + PFN_vkCreateSharedSwapchainsKHR CreateSharedSwapchainsKHR; + PFN_vkDebugMarkerSetObjectTagEXT DebugMarkerSetObjectTagEXT; + PFN_vkDebugMarkerSetObjectNameEXT DebugMarkerSetObjectNameEXT; + PFN_vkCmdDebugMarkerBeginEXT CmdDebugMarkerBeginEXT; + PFN_vkCmdDebugMarkerEndEXT CmdDebugMarkerEndEXT; + PFN_vkCmdDebugMarkerInsertEXT CmdDebugMarkerInsertEXT; + PFN_vkCmdProcessCommandsNVX CmdProcessCommandsNVX; + PFN_vkCmdReserveSpaceForCommandsNVX CmdReserveSpaceForCommandsNVX; + PFN_vkCreateIndirectCommandsLayoutNVX CreateIndirectCommandsLayoutNVX; + PFN_vkDestroyIndirectCommandsLayoutNVX DestroyIndirectCommandsLayoutNVX; + PFN_vkCreateObjectTableNVX CreateObjectTableNVX; + PFN_vkDestroyObjectTableNVX DestroyObjectTableNVX; + PFN_vkRegisterObjectsNVX RegisterObjectsNVX; + PFN_vkUnregisterObjectsNVX UnregisterObjectsNVX; +} VkLayerDispatchTable; + +typedef struct VkLayerInstanceDispatchTable_ { + PFN_vkGetInstanceProcAddr GetInstanceProcAddr; + PFN_vkDestroyInstance DestroyInstance; + PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices; + PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures; + PFN_vkGetPhysicalDeviceImageFormatProperties + GetPhysicalDeviceImageFormatProperties; + PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties + GetPhysicalDeviceSparseImageFormatProperties; + PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceQueueFamilyProperties + GetPhysicalDeviceQueueFamilyProperties; + PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties; + PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; + PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties; + PFN_vkDestroySurfaceKHR DestroySurfaceKHR; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR + GetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR + GetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT; +#ifdef VK_USE_PLATFORM_MIR_KHR + PFN_vkCreateMirSurfaceKHR CreateMirSurfaceKHR; + PFN_vkGetPhysicalDeviceMirPresentationSupportKHR + GetPhysicalDeviceMirPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR; + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR + GetPhysicalDeviceWaylandPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_WIN32_KHR + PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR; + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + GetPhysicalDeviceWin32PresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + PFN_vkCreateXcbSurfaceKHR CreateXcbSurfaceKHR; + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + GetPhysicalDeviceXcbPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_XLIB_KHR + PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR; + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + GetPhysicalDeviceXlibPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_ANDROID_KHR + PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; +#endif + PFN_vkGetPhysicalDeviceDisplayPropertiesKHR + GetPhysicalDeviceDisplayPropertiesKHR; + PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR + GetPhysicalDeviceDisplayPlanePropertiesKHR; + PFN_vkGetDisplayPlaneSupportedDisplaysKHR + GetDisplayPlaneSupportedDisplaysKHR; + PFN_vkGetDisplayModePropertiesKHR + GetDisplayModePropertiesKHR; + PFN_vkCreateDisplayModeKHR + CreateDisplayModeKHR; + PFN_vkGetDisplayPlaneCapabilitiesKHR + GetDisplayPlaneCapabilitiesKHR; + PFN_vkCreateDisplayPlaneSurfaceKHR + CreateDisplayPlaneSurfaceKHR; + PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV + GetPhysicalDeviceExternalImageFormatPropertiesNV; + PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX + GetPhysicalDeviceGeneratedCommandsPropertiesNVX; +} VkLayerInstanceDispatchTable; + +// ------------------------------------------------------------------------------------------------ +// CreateInstance and CreateDevice support structures + +/* Sub type of structure for instance and device loader ext of CreateInfo. + * When sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + * or sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + * then VkLayerFunction indicates struct type pointed to by pNext + */ +typedef enum VkLayerFunction_ { + VK_LAYER_LINK_INFO = 0, + VK_LOADER_DATA_CALLBACK = 1 +} VkLayerFunction; + +typedef struct VkLayerInstanceLink_ { + struct VkLayerInstanceLink_ *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; +} VkLayerInstanceLink; + +/* + * When creating the device chain the loader needs to pass + * down information about it's device structure needed at + * the end of the chain. Passing the data via the + * VkLayerDeviceInfo avoids issues with finding the + * exact instance being used. + */ +typedef struct VkLayerDeviceInfo_ { + void *device_info; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; +} VkLayerDeviceInfo; + +typedef VkResult (VKAPI_PTR *PFN_vkSetInstanceLoaderData)(VkInstance instance, + void *object); +typedef VkResult (VKAPI_PTR *PFN_vkSetDeviceLoaderData)(VkDevice device, + void *object); + +typedef struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerInstanceLink *pLayerInfo; + PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData; + } u; +} VkLayerInstanceCreateInfo; + +typedef struct VkLayerDeviceLink_ { + struct VkLayerDeviceLink_ *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; +} VkLayerDeviceLink; + +typedef struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerDeviceLink *pLayerInfo; + PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData; + } u; +} VkLayerDeviceCreateInfo; + diff --git a/3rdparty/vulkan/include/vulkan/vk_platform.h b/3rdparty/vulkan/include/vulkan/vk_platform.h new file mode 100644 index 0000000..0fa62ee --- /dev/null +++ b/3rdparty/vulkan/include/vulkan/vk_platform.h @@ -0,0 +1,120 @@ +// +// File: vk_platform.h +// +/* +** Copyright (c) 2014-2015 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef VK_PLATFORM_H_ +#define VK_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +/* +*************************************************************************************************** +* Platform-specific directives and type declarations +*************************************************************************************************** +*/ + +/* Platform-specific calling convention macros. + * + * Platforms should define these so that Vulkan clients call Vulkan commands + * with the same calling conventions that the Vulkan implementation expects. + * + * VKAPI_ATTR - Placed before the return type in function declarations. + * Useful for C++11 and GCC/Clang-style function attribute syntax. + * VKAPI_CALL - Placed after the return type in function declarations. + * Useful for MSVC-style calling convention syntax. + * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. + * + * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); + * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); + */ +#if defined(_WIN32) + // On Windows, Vulkan commands use the stdcall convention + #define VKAPI_ATTR + #define VKAPI_CALL __stdcall + #define VKAPI_PTR VKAPI_CALL +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 + #error "Vulkan isn't supported for the 'armeabi' NDK ABI" +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) + // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" + // calling convention, i.e. float parameters are passed in registers. This + // is true even if the rest of the application passes floats on the stack, + // as it does by default when compiling for the armeabi-v7a NDK ABI. + #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) + #define VKAPI_CALL + #define VKAPI_PTR VKAPI_ATTR +#else + // On other platforms, use the default calling convention + #define VKAPI_ATTR + #define VKAPI_CALL + #define VKAPI_PTR +#endif + +#include + +#if !defined(VK_NO_STDINT_H) + #if defined(_MSC_VER) && (_MSC_VER < 1600) + typedef signed __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef signed __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef signed __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #else + #include + #endif +#endif // !defined(VK_NO_STDINT_H) + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +// Platform-specific headers required by platform window system extensions. +// These are enabled prior to #including "vulkan.h". The same enable then +// controls inclusion of the extension interfaces in vulkan.h. + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +#include +#endif + +#ifdef VK_USE_PLATFORM_MIR_KHR +#include +#endif + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +#include +#endif + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#include +#endif + +#ifdef VK_USE_PLATFORM_XLIB_KHR +#include +#endif + +#ifdef VK_USE_PLATFORM_XCB_KHR +#include +#endif + +#endif diff --git a/3rdparty/vulkan/include/vulkan/vk_sdk_platform.h b/3rdparty/vulkan/include/vulkan/vk_sdk_platform.h new file mode 100644 index 0000000..ef9a000 --- /dev/null +++ b/3rdparty/vulkan/include/vulkan/vk_sdk_platform.h @@ -0,0 +1,46 @@ +// +// File: vk_sdk_platform.h +// +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VK_SDK_PLATFORM_H +#define VK_SDK_PLATFORM_H + +#if defined(_WIN32) +#define NOMINMAX +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif // __cplusplus + +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) +// C99: +// Microsoft didn't implement C99 in Visual Studio; but started adding it with +// VS2013. However, VS2013 still didn't have snprintf(). The following is a +// work-around (Note: The _CRT_SECURE_NO_WARNINGS macro must be set in the +// "CMakeLists.txt" file). +// NOTE: This is fixed in Visual Studio 2015. +#define snprintf _snprintf +#endif + +#define strdup _strdup + +#endif // _WIN32 + +#endif // VK_SDK_PLATFORM_H diff --git a/3rdparty/vulkan/include/vulkan/vulkan.h b/3rdparty/vulkan/include/vulkan/vulkan.h new file mode 100644 index 0000000..f24a0a2 --- /dev/null +++ b/3rdparty/vulkan/include/vulkan/vulkan.h @@ -0,0 +1,4400 @@ +#ifndef VULKAN_H_ +#define VULKAN_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2015-2016 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#define VK_VERSION_1_0 1 +#include "vk_platform.h" + +#define VK_MAKE_VERSION(major, minor, patch) \ + (((major) << 22) | ((minor) << 12) | (patch)) + +// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. +//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) + +// Vulkan 1.0 version number +#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0) + +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +// Version of this file +#define VK_HEADER_VERSION 38 + + +#define VK_NULL_HANDLE 0 + + + +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; + + +#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif +#endif + + + +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; +typedef uint64_t VkDeviceSize; +typedef uint32_t VkSampleMask; + +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_HANDLE(VkPhysicalDevice) +VK_DEFINE_HANDLE(VkDevice) +VK_DEFINE_HANDLE(VkQueue) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore) +VK_DEFINE_HANDLE(VkCommandBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) + +#define VK_LOD_CLAMP_NONE 1000.0f +#define VK_REMAINING_MIP_LEVELS (~0U) +#define VK_REMAINING_ARRAY_LAYERS (~0U) +#define VK_WHOLE_SIZE (~0ULL) +#define VK_ATTACHMENT_UNUSED (~0U) +#define VK_TRUE 1 +#define VK_FALSE 0 +#define VK_QUEUE_FAMILY_IGNORED (~0U) +#define VK_SUBPASS_EXTERNAL (~0U) +#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 +#define VK_UUID_SIZE 16 +#define VK_MAX_MEMORY_TYPES 32 +#define VK_MAX_MEMORY_HEAPS 16 +#define VK_MAX_EXTENSION_NAME_SIZE 256 +#define VK_MAX_DESCRIPTION_SIZE 256 + + +typedef enum VkPipelineCacheHeaderVersion { + VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, + VK_PIPELINE_CACHE_HEADER_VERSION_BEGIN_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, + VK_PIPELINE_CACHE_HEADER_VERSION_END_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, + VK_PIPELINE_CACHE_HEADER_VERSION_RANGE_SIZE = (VK_PIPELINE_CACHE_HEADER_VERSION_ONE - VK_PIPELINE_CACHE_HEADER_VERSION_ONE + 1), + VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheHeaderVersion; + +typedef enum VkResult { + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_FRAGMENTED_POOL = -12, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_ERROR_INVALID_SHADER_NV = -1000012000, + VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL, + VK_RESULT_END_RANGE = VK_INCOMPLETE, + VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1), + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; + +typedef enum VkStructureType { + VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, + VK_STRUCTURE_TYPE_SUBMIT_INFO = 4, + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5, + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6, + VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7, + VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8, + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9, + VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10, + VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11, + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12, + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13, + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14, + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15, + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16, + VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18, + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28, + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29, + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30, + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35, + VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36, + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38, + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42, + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45, + VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46, + VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47, + VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48, + VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, + VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, + VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, + VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR = 1000003000, + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, + VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, + VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, + VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, + VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, + VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, + VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, + VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, + VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, + VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, + VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; + +typedef enum VkSystemAllocationScope { + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, + VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, + VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, + VK_SYSTEM_ALLOCATION_SCOPE_BEGIN_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, + VK_SYSTEM_ALLOCATION_SCOPE_END_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, + VK_SYSTEM_ALLOCATION_SCOPE_RANGE_SIZE = (VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND + 1), + VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF +} VkSystemAllocationScope; + +typedef enum VkInternalAllocationType { + VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, + VK_INTERNAL_ALLOCATION_TYPE_BEGIN_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, + VK_INTERNAL_ALLOCATION_TYPE_END_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, + VK_INTERNAL_ALLOCATION_TYPE_RANGE_SIZE = (VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + 1), + VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkInternalAllocationType; + +typedef enum VkFormat { + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, + VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), + VK_FORMAT_MAX_ENUM = 0x7FFFFFFF +} VkFormat; + +typedef enum VkImageType { + VK_IMAGE_TYPE_1D = 0, + VK_IMAGE_TYPE_2D = 1, + VK_IMAGE_TYPE_3D = 2, + VK_IMAGE_TYPE_BEGIN_RANGE = VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_END_RANGE = VK_IMAGE_TYPE_3D, + VK_IMAGE_TYPE_RANGE_SIZE = (VK_IMAGE_TYPE_3D - VK_IMAGE_TYPE_1D + 1), + VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageType; + +typedef enum VkImageTiling { + VK_IMAGE_TILING_OPTIMAL = 0, + VK_IMAGE_TILING_LINEAR = 1, + VK_IMAGE_TILING_BEGIN_RANGE = VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_TILING_END_RANGE = VK_IMAGE_TILING_LINEAR, + VK_IMAGE_TILING_RANGE_SIZE = (VK_IMAGE_TILING_LINEAR - VK_IMAGE_TILING_OPTIMAL + 1), + VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF +} VkImageTiling; + +typedef enum VkPhysicalDeviceType { + VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, + VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, + VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, + VK_PHYSICAL_DEVICE_TYPE_CPU = 4, + VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE = VK_PHYSICAL_DEVICE_TYPE_OTHER, + VK_PHYSICAL_DEVICE_TYPE_END_RANGE = VK_PHYSICAL_DEVICE_TYPE_CPU, + VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE = (VK_PHYSICAL_DEVICE_TYPE_CPU - VK_PHYSICAL_DEVICE_TYPE_OTHER + 1), + VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkPhysicalDeviceType; + +typedef enum VkQueryType { + VK_QUERY_TYPE_OCCLUSION = 0, + VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, + VK_QUERY_TYPE_TIMESTAMP = 2, + VK_QUERY_TYPE_BEGIN_RANGE = VK_QUERY_TYPE_OCCLUSION, + VK_QUERY_TYPE_END_RANGE = VK_QUERY_TYPE_TIMESTAMP, + VK_QUERY_TYPE_RANGE_SIZE = (VK_QUERY_TYPE_TIMESTAMP - VK_QUERY_TYPE_OCCLUSION + 1), + VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkQueryType; + +typedef enum VkSharingMode { + VK_SHARING_MODE_EXCLUSIVE = 0, + VK_SHARING_MODE_CONCURRENT = 1, + VK_SHARING_MODE_BEGIN_RANGE = VK_SHARING_MODE_EXCLUSIVE, + VK_SHARING_MODE_END_RANGE = VK_SHARING_MODE_CONCURRENT, + VK_SHARING_MODE_RANGE_SIZE = (VK_SHARING_MODE_CONCURRENT - VK_SHARING_MODE_EXCLUSIVE + 1), + VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSharingMode; + +typedef enum VkImageLayout { + VK_IMAGE_LAYOUT_UNDEFINED = 0, + VK_IMAGE_LAYOUT_GENERAL = 1, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, + VK_IMAGE_LAYOUT_PREINITIALIZED = 8, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, + VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED, + VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1), + VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF +} VkImageLayout; + +typedef enum VkImageViewType { + VK_IMAGE_VIEW_TYPE_1D = 0, + VK_IMAGE_VIEW_TYPE_2D = 1, + VK_IMAGE_VIEW_TYPE_3D = 2, + VK_IMAGE_VIEW_TYPE_CUBE = 3, + VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, + VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, + VK_IMAGE_VIEW_TYPE_BEGIN_RANGE = VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_END_RANGE = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + VK_IMAGE_VIEW_TYPE_RANGE_SIZE = (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY - VK_IMAGE_VIEW_TYPE_1D + 1), + VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageViewType; + +typedef enum VkComponentSwizzle { + VK_COMPONENT_SWIZZLE_IDENTITY = 0, + VK_COMPONENT_SWIZZLE_ZERO = 1, + VK_COMPONENT_SWIZZLE_ONE = 2, + VK_COMPONENT_SWIZZLE_R = 3, + VK_COMPONENT_SWIZZLE_G = 4, + VK_COMPONENT_SWIZZLE_B = 5, + VK_COMPONENT_SWIZZLE_A = 6, + VK_COMPONENT_SWIZZLE_BEGIN_RANGE = VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_END_RANGE = VK_COMPONENT_SWIZZLE_A, + VK_COMPONENT_SWIZZLE_RANGE_SIZE = (VK_COMPONENT_SWIZZLE_A - VK_COMPONENT_SWIZZLE_IDENTITY + 1), + VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF +} VkComponentSwizzle; + +typedef enum VkVertexInputRate { + VK_VERTEX_INPUT_RATE_VERTEX = 0, + VK_VERTEX_INPUT_RATE_INSTANCE = 1, + VK_VERTEX_INPUT_RATE_BEGIN_RANGE = VK_VERTEX_INPUT_RATE_VERTEX, + VK_VERTEX_INPUT_RATE_END_RANGE = VK_VERTEX_INPUT_RATE_INSTANCE, + VK_VERTEX_INPUT_RATE_RANGE_SIZE = (VK_VERTEX_INPUT_RATE_INSTANCE - VK_VERTEX_INPUT_RATE_VERTEX + 1), + VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF +} VkVertexInputRate; + +typedef enum VkPrimitiveTopology { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + VK_PRIMITIVE_TOPOLOGY_BEGIN_RANGE = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + VK_PRIMITIVE_TOPOLOGY_END_RANGE = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, + VK_PRIMITIVE_TOPOLOGY_RANGE_SIZE = (VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - VK_PRIMITIVE_TOPOLOGY_POINT_LIST + 1), + VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF +} VkPrimitiveTopology; + +typedef enum VkPolygonMode { + VK_POLYGON_MODE_FILL = 0, + VK_POLYGON_MODE_LINE = 1, + VK_POLYGON_MODE_POINT = 2, + VK_POLYGON_MODE_BEGIN_RANGE = VK_POLYGON_MODE_FILL, + VK_POLYGON_MODE_END_RANGE = VK_POLYGON_MODE_POINT, + VK_POLYGON_MODE_RANGE_SIZE = (VK_POLYGON_MODE_POINT - VK_POLYGON_MODE_FILL + 1), + VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF +} VkPolygonMode; + +typedef enum VkFrontFace { + VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, + VK_FRONT_FACE_CLOCKWISE = 1, + VK_FRONT_FACE_BEGIN_RANGE = VK_FRONT_FACE_COUNTER_CLOCKWISE, + VK_FRONT_FACE_END_RANGE = VK_FRONT_FACE_CLOCKWISE, + VK_FRONT_FACE_RANGE_SIZE = (VK_FRONT_FACE_CLOCKWISE - VK_FRONT_FACE_COUNTER_CLOCKWISE + 1), + VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF +} VkFrontFace; + +typedef enum VkCompareOp { + VK_COMPARE_OP_NEVER = 0, + VK_COMPARE_OP_LESS = 1, + VK_COMPARE_OP_EQUAL = 2, + VK_COMPARE_OP_LESS_OR_EQUAL = 3, + VK_COMPARE_OP_GREATER = 4, + VK_COMPARE_OP_NOT_EQUAL = 5, + VK_COMPARE_OP_GREATER_OR_EQUAL = 6, + VK_COMPARE_OP_ALWAYS = 7, + VK_COMPARE_OP_BEGIN_RANGE = VK_COMPARE_OP_NEVER, + VK_COMPARE_OP_END_RANGE = VK_COMPARE_OP_ALWAYS, + VK_COMPARE_OP_RANGE_SIZE = (VK_COMPARE_OP_ALWAYS - VK_COMPARE_OP_NEVER + 1), + VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF +} VkCompareOp; + +typedef enum VkStencilOp { + VK_STENCIL_OP_KEEP = 0, + VK_STENCIL_OP_ZERO = 1, + VK_STENCIL_OP_REPLACE = 2, + VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, + VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, + VK_STENCIL_OP_INVERT = 5, + VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, + VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, + VK_STENCIL_OP_BEGIN_RANGE = VK_STENCIL_OP_KEEP, + VK_STENCIL_OP_END_RANGE = VK_STENCIL_OP_DECREMENT_AND_WRAP, + VK_STENCIL_OP_RANGE_SIZE = (VK_STENCIL_OP_DECREMENT_AND_WRAP - VK_STENCIL_OP_KEEP + 1), + VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF +} VkStencilOp; + +typedef enum VkLogicOp { + VK_LOGIC_OP_CLEAR = 0, + VK_LOGIC_OP_AND = 1, + VK_LOGIC_OP_AND_REVERSE = 2, + VK_LOGIC_OP_COPY = 3, + VK_LOGIC_OP_AND_INVERTED = 4, + VK_LOGIC_OP_NO_OP = 5, + VK_LOGIC_OP_XOR = 6, + VK_LOGIC_OP_OR = 7, + VK_LOGIC_OP_NOR = 8, + VK_LOGIC_OP_EQUIVALENT = 9, + VK_LOGIC_OP_INVERT = 10, + VK_LOGIC_OP_OR_REVERSE = 11, + VK_LOGIC_OP_COPY_INVERTED = 12, + VK_LOGIC_OP_OR_INVERTED = 13, + VK_LOGIC_OP_NAND = 14, + VK_LOGIC_OP_SET = 15, + VK_LOGIC_OP_BEGIN_RANGE = VK_LOGIC_OP_CLEAR, + VK_LOGIC_OP_END_RANGE = VK_LOGIC_OP_SET, + VK_LOGIC_OP_RANGE_SIZE = (VK_LOGIC_OP_SET - VK_LOGIC_OP_CLEAR + 1), + VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF +} VkLogicOp; + +typedef enum VkBlendFactor { + VK_BLEND_FACTOR_ZERO = 0, + VK_BLEND_FACTOR_ONE = 1, + VK_BLEND_FACTOR_SRC_COLOR = 2, + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3, + VK_BLEND_FACTOR_DST_COLOR = 4, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5, + VK_BLEND_FACTOR_SRC_ALPHA = 6, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7, + VK_BLEND_FACTOR_DST_ALPHA = 8, + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9, + VK_BLEND_FACTOR_CONSTANT_COLOR = 10, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11, + VK_BLEND_FACTOR_CONSTANT_ALPHA = 12, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13, + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14, + VK_BLEND_FACTOR_SRC1_COLOR = 15, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, + VK_BLEND_FACTOR_SRC1_ALPHA = 17, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, + VK_BLEND_FACTOR_BEGIN_RANGE = VK_BLEND_FACTOR_ZERO, + VK_BLEND_FACTOR_END_RANGE = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, + VK_BLEND_FACTOR_RANGE_SIZE = (VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA - VK_BLEND_FACTOR_ZERO + 1), + VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF +} VkBlendFactor; + +typedef enum VkBlendOp { + VK_BLEND_OP_ADD = 0, + VK_BLEND_OP_SUBTRACT = 1, + VK_BLEND_OP_REVERSE_SUBTRACT = 2, + VK_BLEND_OP_MIN = 3, + VK_BLEND_OP_MAX = 4, + VK_BLEND_OP_BEGIN_RANGE = VK_BLEND_OP_ADD, + VK_BLEND_OP_END_RANGE = VK_BLEND_OP_MAX, + VK_BLEND_OP_RANGE_SIZE = (VK_BLEND_OP_MAX - VK_BLEND_OP_ADD + 1), + VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF +} VkBlendOp; + +typedef enum VkDynamicState { + VK_DYNAMIC_STATE_VIEWPORT = 0, + VK_DYNAMIC_STATE_SCISSOR = 1, + VK_DYNAMIC_STATE_LINE_WIDTH = 2, + VK_DYNAMIC_STATE_DEPTH_BIAS = 3, + VK_DYNAMIC_STATE_BLEND_CONSTANTS = 4, + VK_DYNAMIC_STATE_DEPTH_BOUNDS = 5, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, + VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, + VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE, + VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1), + VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF +} VkDynamicState; + +typedef enum VkFilter { + VK_FILTER_NEAREST = 0, + VK_FILTER_LINEAR = 1, + VK_FILTER_CUBIC_IMG = 1000015000, + VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST, + VK_FILTER_END_RANGE = VK_FILTER_LINEAR, + VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1), + VK_FILTER_MAX_ENUM = 0x7FFFFFFF +} VkFilter; + +typedef enum VkSamplerMipmapMode { + VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, + VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, + VK_SAMPLER_MIPMAP_MODE_BEGIN_RANGE = VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_MIPMAP_MODE_END_RANGE = VK_SAMPLER_MIPMAP_MODE_LINEAR, + VK_SAMPLER_MIPMAP_MODE_RANGE_SIZE = (VK_SAMPLER_MIPMAP_MODE_LINEAR - VK_SAMPLER_MIPMAP_MODE_NEAREST + 1), + VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerMipmapMode; + +typedef enum VkSamplerAddressMode { + VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, + VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), + VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerAddressMode; + +typedef enum VkBorderColor { + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1, + VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, + VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, + VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, + VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, + VK_BORDER_COLOR_BEGIN_RANGE = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_END_RANGE = VK_BORDER_COLOR_INT_OPAQUE_WHITE, + VK_BORDER_COLOR_RANGE_SIZE = (VK_BORDER_COLOR_INT_OPAQUE_WHITE - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK + 1), + VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF +} VkBorderColor; + +typedef enum VkDescriptorType { + VK_DESCRIPTOR_TYPE_SAMPLER = 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, + VK_DESCRIPTOR_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_TYPE_SAMPLER, + VK_DESCRIPTOR_TYPE_END_RANGE = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_DESCRIPTOR_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1), + VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorType; + +typedef enum VkAttachmentLoadOp { + VK_ATTACHMENT_LOAD_OP_LOAD = 0, + VK_ATTACHMENT_LOAD_OP_CLEAR = 1, + VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, + VK_ATTACHMENT_LOAD_OP_BEGIN_RANGE = VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_END_RANGE = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_LOAD_OP_RANGE_SIZE = (VK_ATTACHMENT_LOAD_OP_DONT_CARE - VK_ATTACHMENT_LOAD_OP_LOAD + 1), + VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentLoadOp; + +typedef enum VkAttachmentStoreOp { + VK_ATTACHMENT_STORE_OP_STORE = 0, + VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, + VK_ATTACHMENT_STORE_OP_BEGIN_RANGE = VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_STORE_OP_END_RANGE = VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_RANGE_SIZE = (VK_ATTACHMENT_STORE_OP_DONT_CARE - VK_ATTACHMENT_STORE_OP_STORE + 1), + VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentStoreOp; + +typedef enum VkPipelineBindPoint { + VK_PIPELINE_BIND_POINT_GRAPHICS = 0, + VK_PIPELINE_BIND_POINT_COMPUTE = 1, + VK_PIPELINE_BIND_POINT_BEGIN_RANGE = VK_PIPELINE_BIND_POINT_GRAPHICS, + VK_PIPELINE_BIND_POINT_END_RANGE = VK_PIPELINE_BIND_POINT_COMPUTE, + VK_PIPELINE_BIND_POINT_RANGE_SIZE = (VK_PIPELINE_BIND_POINT_COMPUTE - VK_PIPELINE_BIND_POINT_GRAPHICS + 1), + VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF +} VkPipelineBindPoint; + +typedef enum VkCommandBufferLevel { + VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, + VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, + VK_COMMAND_BUFFER_LEVEL_BEGIN_RANGE = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + VK_COMMAND_BUFFER_LEVEL_END_RANGE = VK_COMMAND_BUFFER_LEVEL_SECONDARY, + VK_COMMAND_BUFFER_LEVEL_RANGE_SIZE = (VK_COMMAND_BUFFER_LEVEL_SECONDARY - VK_COMMAND_BUFFER_LEVEL_PRIMARY + 1), + VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferLevel; + +typedef enum VkIndexType { + VK_INDEX_TYPE_UINT16 = 0, + VK_INDEX_TYPE_UINT32 = 1, + VK_INDEX_TYPE_BEGIN_RANGE = VK_INDEX_TYPE_UINT16, + VK_INDEX_TYPE_END_RANGE = VK_INDEX_TYPE_UINT32, + VK_INDEX_TYPE_RANGE_SIZE = (VK_INDEX_TYPE_UINT32 - VK_INDEX_TYPE_UINT16 + 1), + VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkIndexType; + +typedef enum VkSubpassContents { + VK_SUBPASS_CONTENTS_INLINE = 0, + VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, + VK_SUBPASS_CONTENTS_BEGIN_RANGE = VK_SUBPASS_CONTENTS_INLINE, + VK_SUBPASS_CONTENTS_END_RANGE = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, + VK_SUBPASS_CONTENTS_RANGE_SIZE = (VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS - VK_SUBPASS_CONTENTS_INLINE + 1), + VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF +} VkSubpassContents; + +typedef VkFlags VkInstanceCreateFlags; + +typedef enum VkFormatFeatureFlagBits { + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001, + VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 0x00000002, + VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004, + VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 0x00000010, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020, + VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 0x00000040, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 0x00000080, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200, + VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400, + VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, + VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFormatFeatureFlagBits; +typedef VkFlags VkFormatFeatureFlags; + +typedef enum VkImageUsageFlagBits { + VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, + VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, + VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, + VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageUsageFlagBits; +typedef VkFlags VkImageUsageFlags; + +typedef enum VkImageCreateFlagBits { + VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001, + VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, + VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, + VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, + VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageCreateFlagBits; +typedef VkFlags VkImageCreateFlags; + +typedef enum VkSampleCountFlagBits { + VK_SAMPLE_COUNT_1_BIT = 0x00000001, + VK_SAMPLE_COUNT_2_BIT = 0x00000002, + VK_SAMPLE_COUNT_4_BIT = 0x00000004, + VK_SAMPLE_COUNT_8_BIT = 0x00000008, + VK_SAMPLE_COUNT_16_BIT = 0x00000010, + VK_SAMPLE_COUNT_32_BIT = 0x00000020, + VK_SAMPLE_COUNT_64_BIT = 0x00000040, + VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSampleCountFlagBits; +typedef VkFlags VkSampleCountFlags; + +typedef enum VkQueueFlagBits { + VK_QUEUE_GRAPHICS_BIT = 0x00000001, + VK_QUEUE_COMPUTE_BIT = 0x00000002, + VK_QUEUE_TRANSFER_BIT = 0x00000004, + VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueueFlagBits; +typedef VkFlags VkQueueFlags; + +typedef enum VkMemoryPropertyFlagBits { + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, + VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryPropertyFlagBits; +typedef VkFlags VkMemoryPropertyFlags; + +typedef enum VkMemoryHeapFlagBits { + VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryHeapFlagBits; +typedef VkFlags VkMemoryHeapFlags; +typedef VkFlags VkDeviceCreateFlags; +typedef VkFlags VkDeviceQueueCreateFlags; + +typedef enum VkPipelineStageFlagBits { + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001, + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008, + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010, + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020, + VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800, + VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000, + VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, + VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, + VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineStageFlagBits; +typedef VkFlags VkPipelineStageFlags; +typedef VkFlags VkMemoryMapFlags; + +typedef enum VkImageAspectFlagBits { + VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, + VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, + VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, + VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageAspectFlagBits; +typedef VkFlags VkImageAspectFlags; + +typedef enum VkSparseImageFormatFlagBits { + VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, + VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002, + VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004, + VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseImageFormatFlagBits; +typedef VkFlags VkSparseImageFormatFlags; + +typedef enum VkSparseMemoryBindFlagBits { + VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseMemoryBindFlagBits; +typedef VkFlags VkSparseMemoryBindFlags; + +typedef enum VkFenceCreateFlagBits { + VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, + VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFenceCreateFlagBits; +typedef VkFlags VkFenceCreateFlags; +typedef VkFlags VkSemaphoreCreateFlags; +typedef VkFlags VkEventCreateFlags; +typedef VkFlags VkQueryPoolCreateFlags; + +typedef enum VkQueryPipelineStatisticFlagBits { + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001, + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 0x00000002, + VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 0x00000004, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 0x00000008, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 0x00000010, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 0x00000020, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 0x00000040, + VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 0x00000080, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200, + VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400, + VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryPipelineStatisticFlagBits; +typedef VkFlags VkQueryPipelineStatisticFlags; + +typedef enum VkQueryResultFlagBits { + VK_QUERY_RESULT_64_BIT = 0x00000001, + VK_QUERY_RESULT_WAIT_BIT = 0x00000002, + VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, + VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, + VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryResultFlagBits; +typedef VkFlags VkQueryResultFlags; + +typedef enum VkBufferCreateFlagBits { + VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001, + VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, + VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, + VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferCreateFlagBits; +typedef VkFlags VkBufferCreateFlags; + +typedef enum VkBufferUsageFlagBits { + VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001, + VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002, + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004, + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, + VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferUsageFlagBits; +typedef VkFlags VkBufferUsageFlags; +typedef VkFlags VkBufferViewCreateFlags; +typedef VkFlags VkImageViewCreateFlags; +typedef VkFlags VkShaderModuleCreateFlags; +typedef VkFlags VkPipelineCacheCreateFlags; + +typedef enum VkPipelineCreateFlagBits { + VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, + VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, + VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, + VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCreateFlagBits; +typedef VkFlags VkPipelineCreateFlags; +typedef VkFlags VkPipelineShaderStageCreateFlags; + +typedef enum VkShaderStageFlagBits { + VK_SHADER_STAGE_VERTEX_BIT = 0x00000001, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, + VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, + VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, + VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, + VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, + VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkShaderStageFlagBits; +typedef VkFlags VkPipelineVertexInputStateCreateFlags; +typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; +typedef VkFlags VkPipelineTessellationStateCreateFlags; +typedef VkFlags VkPipelineViewportStateCreateFlags; +typedef VkFlags VkPipelineRasterizationStateCreateFlags; + +typedef enum VkCullModeFlagBits { + VK_CULL_MODE_NONE = 0, + VK_CULL_MODE_FRONT_BIT = 0x00000001, + VK_CULL_MODE_BACK_BIT = 0x00000002, + VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, + VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCullModeFlagBits; +typedef VkFlags VkCullModeFlags; +typedef VkFlags VkPipelineMultisampleStateCreateFlags; +typedef VkFlags VkPipelineDepthStencilStateCreateFlags; +typedef VkFlags VkPipelineColorBlendStateCreateFlags; + +typedef enum VkColorComponentFlagBits { + VK_COLOR_COMPONENT_R_BIT = 0x00000001, + VK_COLOR_COMPONENT_G_BIT = 0x00000002, + VK_COLOR_COMPONENT_B_BIT = 0x00000004, + VK_COLOR_COMPONENT_A_BIT = 0x00000008, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkColorComponentFlagBits; +typedef VkFlags VkColorComponentFlags; +typedef VkFlags VkPipelineDynamicStateCreateFlags; +typedef VkFlags VkPipelineLayoutCreateFlags; +typedef VkFlags VkShaderStageFlags; +typedef VkFlags VkSamplerCreateFlags; +typedef VkFlags VkDescriptorSetLayoutCreateFlags; + +typedef enum VkDescriptorPoolCreateFlagBits { + VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, + VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorPoolCreateFlagBits; +typedef VkFlags VkDescriptorPoolCreateFlags; +typedef VkFlags VkDescriptorPoolResetFlags; +typedef VkFlags VkFramebufferCreateFlags; +typedef VkFlags VkRenderPassCreateFlags; + +typedef enum VkAttachmentDescriptionFlagBits { + VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, + VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentDescriptionFlagBits; +typedef VkFlags VkAttachmentDescriptionFlags; +typedef VkFlags VkSubpassDescriptionFlags; + +typedef enum VkAccessFlagBits { + VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, + VK_ACCESS_INDEX_READ_BIT = 0x00000002, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, + VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, + VK_ACCESS_SHADER_READ_BIT = 0x00000020, + VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, + VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, + VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, + VK_ACCESS_HOST_READ_BIT = 0x00002000, + VK_ACCESS_HOST_WRITE_BIT = 0x00004000, + VK_ACCESS_MEMORY_READ_BIT = 0x00008000, + VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, + VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAccessFlagBits; +typedef VkFlags VkAccessFlags; + +typedef enum VkDependencyFlagBits { + VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, + VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDependencyFlagBits; +typedef VkFlags VkDependencyFlags; + +typedef enum VkCommandPoolCreateFlagBits { + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, + VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolCreateFlagBits; +typedef VkFlags VkCommandPoolCreateFlags; + +typedef enum VkCommandPoolResetFlagBits { + VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolResetFlagBits; +typedef VkFlags VkCommandPoolResetFlags; + +typedef enum VkCommandBufferUsageFlagBits { + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001, + VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002, + VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004, + VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferUsageFlagBits; +typedef VkFlags VkCommandBufferUsageFlags; + +typedef enum VkQueryControlFlagBits { + VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001, + VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryControlFlagBits; +typedef VkFlags VkQueryControlFlags; + +typedef enum VkCommandBufferResetFlagBits { + VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferResetFlagBits; +typedef VkFlags VkCommandBufferResetFlags; + +typedef enum VkStencilFaceFlagBits { + VK_STENCIL_FACE_FRONT_BIT = 0x00000001, + VK_STENCIL_FACE_BACK_BIT = 0x00000002, + VK_STENCIL_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkStencilFaceFlagBits; +typedef VkFlags VkStencilFaceFlags; + +typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( + void* pUserData, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); + +typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( + void* pUserData, + void* pOriginal, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkFreeFunction)( + void* pUserData, + void* pMemory); + +typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); + +typedef struct VkApplicationInfo { + VkStructureType sType; + const void* pNext; + const char* pApplicationName; + uint32_t applicationVersion; + const char* pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; +} VkApplicationInfo; + +typedef struct VkInstanceCreateInfo { + VkStructureType sType; + const void* pNext; + VkInstanceCreateFlags flags; + const VkApplicationInfo* pApplicationInfo; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; +} VkInstanceCreateInfo; + +typedef struct VkAllocationCallbacks { + void* pUserData; + PFN_vkAllocationFunction pfnAllocation; + PFN_vkReallocationFunction pfnReallocation; + PFN_vkFreeFunction pfnFree; + PFN_vkInternalAllocationNotification pfnInternalAllocation; + PFN_vkInternalFreeNotification pfnInternalFree; +} VkAllocationCallbacks; + +typedef struct VkPhysicalDeviceFeatures { + VkBool32 robustBufferAccess; + VkBool32 fullDrawIndexUint32; + VkBool32 imageCubeArray; + VkBool32 independentBlend; + VkBool32 geometryShader; + VkBool32 tessellationShader; + VkBool32 sampleRateShading; + VkBool32 dualSrcBlend; + VkBool32 logicOp; + VkBool32 multiDrawIndirect; + VkBool32 drawIndirectFirstInstance; + VkBool32 depthClamp; + VkBool32 depthBiasClamp; + VkBool32 fillModeNonSolid; + VkBool32 depthBounds; + VkBool32 wideLines; + VkBool32 largePoints; + VkBool32 alphaToOne; + VkBool32 multiViewport; + VkBool32 samplerAnisotropy; + VkBool32 textureCompressionETC2; + VkBool32 textureCompressionASTC_LDR; + VkBool32 textureCompressionBC; + VkBool32 occlusionQueryPrecise; + VkBool32 pipelineStatisticsQuery; + VkBool32 vertexPipelineStoresAndAtomics; + VkBool32 fragmentStoresAndAtomics; + VkBool32 shaderTessellationAndGeometryPointSize; + VkBool32 shaderImageGatherExtended; + VkBool32 shaderStorageImageExtendedFormats; + VkBool32 shaderStorageImageMultisample; + VkBool32 shaderStorageImageReadWithoutFormat; + VkBool32 shaderStorageImageWriteWithoutFormat; + VkBool32 shaderUniformBufferArrayDynamicIndexing; + VkBool32 shaderSampledImageArrayDynamicIndexing; + VkBool32 shaderStorageBufferArrayDynamicIndexing; + VkBool32 shaderStorageImageArrayDynamicIndexing; + VkBool32 shaderClipDistance; + VkBool32 shaderCullDistance; + VkBool32 shaderFloat64; + VkBool32 shaderInt64; + VkBool32 shaderInt16; + VkBool32 shaderResourceResidency; + VkBool32 shaderResourceMinLod; + VkBool32 sparseBinding; + VkBool32 sparseResidencyBuffer; + VkBool32 sparseResidencyImage2D; + VkBool32 sparseResidencyImage3D; + VkBool32 sparseResidency2Samples; + VkBool32 sparseResidency4Samples; + VkBool32 sparseResidency8Samples; + VkBool32 sparseResidency16Samples; + VkBool32 sparseResidencyAliased; + VkBool32 variableMultisampleRate; + VkBool32 inheritedQueries; +} VkPhysicalDeviceFeatures; + +typedef struct VkFormatProperties { + VkFormatFeatureFlags linearTilingFeatures; + VkFormatFeatureFlags optimalTilingFeatures; + VkFormatFeatureFlags bufferFeatures; +} VkFormatProperties; + +typedef struct VkExtent3D { + uint32_t width; + uint32_t height; + uint32_t depth; +} VkExtent3D; + +typedef struct VkImageFormatProperties { + VkExtent3D maxExtent; + uint32_t maxMipLevels; + uint32_t maxArrayLayers; + VkSampleCountFlags sampleCounts; + VkDeviceSize maxResourceSize; +} VkImageFormatProperties; + +typedef struct VkPhysicalDeviceLimits { + uint32_t maxImageDimension1D; + uint32_t maxImageDimension2D; + uint32_t maxImageDimension3D; + uint32_t maxImageDimensionCube; + uint32_t maxImageArrayLayers; + uint32_t maxTexelBufferElements; + uint32_t maxUniformBufferRange; + uint32_t maxStorageBufferRange; + uint32_t maxPushConstantsSize; + uint32_t maxMemoryAllocationCount; + uint32_t maxSamplerAllocationCount; + VkDeviceSize bufferImageGranularity; + VkDeviceSize sparseAddressSpaceSize; + uint32_t maxBoundDescriptorSets; + uint32_t maxPerStageDescriptorSamplers; + uint32_t maxPerStageDescriptorUniformBuffers; + uint32_t maxPerStageDescriptorStorageBuffers; + uint32_t maxPerStageDescriptorSampledImages; + uint32_t maxPerStageDescriptorStorageImages; + uint32_t maxPerStageDescriptorInputAttachments; + uint32_t maxPerStageResources; + uint32_t maxDescriptorSetSamplers; + uint32_t maxDescriptorSetUniformBuffers; + uint32_t maxDescriptorSetUniformBuffersDynamic; + uint32_t maxDescriptorSetStorageBuffers; + uint32_t maxDescriptorSetStorageBuffersDynamic; + uint32_t maxDescriptorSetSampledImages; + uint32_t maxDescriptorSetStorageImages; + uint32_t maxDescriptorSetInputAttachments; + uint32_t maxVertexInputAttributes; + uint32_t maxVertexInputBindings; + uint32_t maxVertexInputAttributeOffset; + uint32_t maxVertexInputBindingStride; + uint32_t maxVertexOutputComponents; + uint32_t maxTessellationGenerationLevel; + uint32_t maxTessellationPatchSize; + uint32_t maxTessellationControlPerVertexInputComponents; + uint32_t maxTessellationControlPerVertexOutputComponents; + uint32_t maxTessellationControlPerPatchOutputComponents; + uint32_t maxTessellationControlTotalOutputComponents; + uint32_t maxTessellationEvaluationInputComponents; + uint32_t maxTessellationEvaluationOutputComponents; + uint32_t maxGeometryShaderInvocations; + uint32_t maxGeometryInputComponents; + uint32_t maxGeometryOutputComponents; + uint32_t maxGeometryOutputVertices; + uint32_t maxGeometryTotalOutputComponents; + uint32_t maxFragmentInputComponents; + uint32_t maxFragmentOutputAttachments; + uint32_t maxFragmentDualSrcAttachments; + uint32_t maxFragmentCombinedOutputResources; + uint32_t maxComputeSharedMemorySize; + uint32_t maxComputeWorkGroupCount[3]; + uint32_t maxComputeWorkGroupInvocations; + uint32_t maxComputeWorkGroupSize[3]; + uint32_t subPixelPrecisionBits; + uint32_t subTexelPrecisionBits; + uint32_t mipmapPrecisionBits; + uint32_t maxDrawIndexedIndexValue; + uint32_t maxDrawIndirectCount; + float maxSamplerLodBias; + float maxSamplerAnisotropy; + uint32_t maxViewports; + uint32_t maxViewportDimensions[2]; + float viewportBoundsRange[2]; + uint32_t viewportSubPixelBits; + size_t minMemoryMapAlignment; + VkDeviceSize minTexelBufferOffsetAlignment; + VkDeviceSize minUniformBufferOffsetAlignment; + VkDeviceSize minStorageBufferOffsetAlignment; + int32_t minTexelOffset; + uint32_t maxTexelOffset; + int32_t minTexelGatherOffset; + uint32_t maxTexelGatherOffset; + float minInterpolationOffset; + float maxInterpolationOffset; + uint32_t subPixelInterpolationOffsetBits; + uint32_t maxFramebufferWidth; + uint32_t maxFramebufferHeight; + uint32_t maxFramebufferLayers; + VkSampleCountFlags framebufferColorSampleCounts; + VkSampleCountFlags framebufferDepthSampleCounts; + VkSampleCountFlags framebufferStencilSampleCounts; + VkSampleCountFlags framebufferNoAttachmentsSampleCounts; + uint32_t maxColorAttachments; + VkSampleCountFlags sampledImageColorSampleCounts; + VkSampleCountFlags sampledImageIntegerSampleCounts; + VkSampleCountFlags sampledImageDepthSampleCounts; + VkSampleCountFlags sampledImageStencilSampleCounts; + VkSampleCountFlags storageImageSampleCounts; + uint32_t maxSampleMaskWords; + VkBool32 timestampComputeAndGraphics; + float timestampPeriod; + uint32_t maxClipDistances; + uint32_t maxCullDistances; + uint32_t maxCombinedClipAndCullDistances; + uint32_t discreteQueuePriorities; + float pointSizeRange[2]; + float lineWidthRange[2]; + float pointSizeGranularity; + float lineWidthGranularity; + VkBool32 strictLines; + VkBool32 standardSampleLocations; + VkDeviceSize optimalBufferCopyOffsetAlignment; + VkDeviceSize optimalBufferCopyRowPitchAlignment; + VkDeviceSize nonCoherentAtomSize; +} VkPhysicalDeviceLimits; + +typedef struct VkPhysicalDeviceSparseProperties { + VkBool32 residencyStandard2DBlockShape; + VkBool32 residencyStandard2DMultisampleBlockShape; + VkBool32 residencyStandard3DBlockShape; + VkBool32 residencyAlignedMipSize; + VkBool32 residencyNonResidentStrict; +} VkPhysicalDeviceSparseProperties; + +typedef struct VkPhysicalDeviceProperties { + uint32_t apiVersion; + uint32_t driverVersion; + uint32_t vendorID; + uint32_t deviceID; + VkPhysicalDeviceType deviceType; + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; + uint8_t pipelineCacheUUID[VK_UUID_SIZE]; + VkPhysicalDeviceLimits limits; + VkPhysicalDeviceSparseProperties sparseProperties; +} VkPhysicalDeviceProperties; + +typedef struct VkQueueFamilyProperties { + VkQueueFlags queueFlags; + uint32_t queueCount; + uint32_t timestampValidBits; + VkExtent3D minImageTransferGranularity; +} VkQueueFamilyProperties; + +typedef struct VkMemoryType { + VkMemoryPropertyFlags propertyFlags; + uint32_t heapIndex; +} VkMemoryType; + +typedef struct VkMemoryHeap { + VkDeviceSize size; + VkMemoryHeapFlags flags; +} VkMemoryHeap; + +typedef struct VkPhysicalDeviceMemoryProperties { + uint32_t memoryTypeCount; + VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; + uint32_t memoryHeapCount; + VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; +} VkPhysicalDeviceMemoryProperties; + +typedef struct VkDeviceQueueCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float* pQueuePriorities; +} VkDeviceQueueCreateInfo; + +typedef struct VkDeviceCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceCreateFlags flags; + uint32_t queueCreateInfoCount; + const VkDeviceQueueCreateInfo* pQueueCreateInfos; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + const VkPhysicalDeviceFeatures* pEnabledFeatures; +} VkDeviceCreateInfo; + +typedef struct VkExtensionProperties { + char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef struct VkLayerProperties { + char layerName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; + uint32_t implementationVersion; + char description[VK_MAX_DESCRIPTION_SIZE]; +} VkLayerProperties; + +typedef struct VkSubmitInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + const VkPipelineStageFlags* pWaitDstStageMask; + uint32_t commandBufferCount; + const VkCommandBuffer* pCommandBuffers; + uint32_t signalSemaphoreCount; + const VkSemaphore* pSignalSemaphores; +} VkSubmitInfo; + +typedef struct VkMemoryAllocateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; +} VkMemoryAllocateInfo; + +typedef struct VkMappedMemoryRange { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkDeviceSize offset; + VkDeviceSize size; +} VkMappedMemoryRange; + +typedef struct VkMemoryRequirements { + VkDeviceSize size; + VkDeviceSize alignment; + uint32_t memoryTypeBits; +} VkMemoryRequirements; + +typedef struct VkSparseImageFormatProperties { + VkImageAspectFlags aspectMask; + VkExtent3D imageGranularity; + VkSparseImageFormatFlags flags; +} VkSparseImageFormatProperties; + +typedef struct VkSparseImageMemoryRequirements { + VkSparseImageFormatProperties formatProperties; + uint32_t imageMipTailFirstLod; + VkDeviceSize imageMipTailSize; + VkDeviceSize imageMipTailOffset; + VkDeviceSize imageMipTailStride; +} VkSparseImageMemoryRequirements; + +typedef struct VkSparseMemoryBind { + VkDeviceSize resourceOffset; + VkDeviceSize size; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseMemoryBind; + +typedef struct VkSparseBufferMemoryBindInfo { + VkBuffer buffer; + uint32_t bindCount; + const VkSparseMemoryBind* pBinds; +} VkSparseBufferMemoryBindInfo; + +typedef struct VkSparseImageOpaqueMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseMemoryBind* pBinds; +} VkSparseImageOpaqueMemoryBindInfo; + +typedef struct VkImageSubresource { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t arrayLayer; +} VkImageSubresource; + +typedef struct VkOffset3D { + int32_t x; + int32_t y; + int32_t z; +} VkOffset3D; + +typedef struct VkSparseImageMemoryBind { + VkImageSubresource subresource; + VkOffset3D offset; + VkExtent3D extent; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseImageMemoryBind; + +typedef struct VkSparseImageMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseImageMemoryBind* pBinds; +} VkSparseImageMemoryBindInfo; + +typedef struct VkBindSparseInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + uint32_t bufferBindCount; + const VkSparseBufferMemoryBindInfo* pBufferBinds; + uint32_t imageOpaqueBindCount; + const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; + uint32_t imageBindCount; + const VkSparseImageMemoryBindInfo* pImageBinds; + uint32_t signalSemaphoreCount; + const VkSemaphore* pSignalSemaphores; +} VkBindSparseInfo; + +typedef struct VkFenceCreateInfo { + VkStructureType sType; + const void* pNext; + VkFenceCreateFlags flags; +} VkFenceCreateInfo; + +typedef struct VkSemaphoreCreateInfo { + VkStructureType sType; + const void* pNext; + VkSemaphoreCreateFlags flags; +} VkSemaphoreCreateInfo; + +typedef struct VkEventCreateInfo { + VkStructureType sType; + const void* pNext; + VkEventCreateFlags flags; +} VkEventCreateInfo; + +typedef struct VkQueryPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkQueryPoolCreateFlags flags; + VkQueryType queryType; + uint32_t queryCount; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkQueryPoolCreateInfo; + +typedef struct VkBufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkBufferCreateFlags flags; + VkDeviceSize size; + VkBufferUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; +} VkBufferCreateInfo; + +typedef struct VkBufferViewCreateInfo { + VkStructureType sType; + const void* pNext; + VkBufferViewCreateFlags flags; + VkBuffer buffer; + VkFormat format; + VkDeviceSize offset; + VkDeviceSize range; +} VkBufferViewCreateInfo; + +typedef struct VkImageCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageCreateFlags flags; + VkImageType imageType; + VkFormat format; + VkExtent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + VkSampleCountFlagBits samples; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + VkImageLayout initialLayout; +} VkImageCreateInfo; + +typedef struct VkSubresourceLayout { + VkDeviceSize offset; + VkDeviceSize size; + VkDeviceSize rowPitch; + VkDeviceSize arrayPitch; + VkDeviceSize depthPitch; +} VkSubresourceLayout; + +typedef struct VkComponentMapping { + VkComponentSwizzle r; + VkComponentSwizzle g; + VkComponentSwizzle b; + VkComponentSwizzle a; +} VkComponentMapping; + +typedef struct VkImageSubresourceRange { + VkImageAspectFlags aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceRange; + +typedef struct VkImageViewCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageViewCreateFlags flags; + VkImage image; + VkImageViewType viewType; + VkFormat format; + VkComponentMapping components; + VkImageSubresourceRange subresourceRange; +} VkImageViewCreateInfo; + +typedef struct VkShaderModuleCreateInfo { + VkStructureType sType; + const void* pNext; + VkShaderModuleCreateFlags flags; + size_t codeSize; + const uint32_t* pCode; +} VkShaderModuleCreateInfo; + +typedef struct VkPipelineCacheCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCacheCreateFlags flags; + size_t initialDataSize; + const void* pInitialData; +} VkPipelineCacheCreateInfo; + +typedef struct VkSpecializationMapEntry { + uint32_t constantID; + uint32_t offset; + size_t size; +} VkSpecializationMapEntry; + +typedef struct VkSpecializationInfo { + uint32_t mapEntryCount; + const VkSpecializationMapEntry* pMapEntries; + size_t dataSize; + const void* pData; +} VkSpecializationInfo; + +typedef struct VkPipelineShaderStageCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineShaderStageCreateFlags flags; + VkShaderStageFlagBits stage; + VkShaderModule module; + const char* pName; + const VkSpecializationInfo* pSpecializationInfo; +} VkPipelineShaderStageCreateInfo; + +typedef struct VkVertexInputBindingDescription { + uint32_t binding; + uint32_t stride; + VkVertexInputRate inputRate; +} VkVertexInputBindingDescription; + +typedef struct VkVertexInputAttributeDescription { + uint32_t location; + uint32_t binding; + VkFormat format; + uint32_t offset; +} VkVertexInputAttributeDescription; + +typedef struct VkPipelineVertexInputStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineVertexInputStateCreateFlags flags; + uint32_t vertexBindingDescriptionCount; + const VkVertexInputBindingDescription* pVertexBindingDescriptions; + uint32_t vertexAttributeDescriptionCount; + const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; +} VkPipelineVertexInputStateCreateInfo; + +typedef struct VkPipelineInputAssemblyStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineInputAssemblyStateCreateFlags flags; + VkPrimitiveTopology topology; + VkBool32 primitiveRestartEnable; +} VkPipelineInputAssemblyStateCreateInfo; + +typedef struct VkPipelineTessellationStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineTessellationStateCreateFlags flags; + uint32_t patchControlPoints; +} VkPipelineTessellationStateCreateInfo; + +typedef struct VkViewport { + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; +} VkViewport; + +typedef struct VkOffset2D { + int32_t x; + int32_t y; +} VkOffset2D; + +typedef struct VkExtent2D { + uint32_t width; + uint32_t height; +} VkExtent2D; + +typedef struct VkRect2D { + VkOffset2D offset; + VkExtent2D extent; +} VkRect2D; + +typedef struct VkPipelineViewportStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineViewportStateCreateFlags flags; + uint32_t viewportCount; + const VkViewport* pViewports; + uint32_t scissorCount; + const VkRect2D* pScissors; +} VkPipelineViewportStateCreateInfo; + +typedef struct VkPipelineRasterizationStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationStateCreateFlags flags; + VkBool32 depthClampEnable; + VkBool32 rasterizerDiscardEnable; + VkPolygonMode polygonMode; + VkCullModeFlags cullMode; + VkFrontFace frontFace; + VkBool32 depthBiasEnable; + float depthBiasConstantFactor; + float depthBiasClamp; + float depthBiasSlopeFactor; + float lineWidth; +} VkPipelineRasterizationStateCreateInfo; + +typedef struct VkPipelineMultisampleStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineMultisampleStateCreateFlags flags; + VkSampleCountFlagBits rasterizationSamples; + VkBool32 sampleShadingEnable; + float minSampleShading; + const VkSampleMask* pSampleMask; + VkBool32 alphaToCoverageEnable; + VkBool32 alphaToOneEnable; +} VkPipelineMultisampleStateCreateInfo; + +typedef struct VkStencilOpState { + VkStencilOp failOp; + VkStencilOp passOp; + VkStencilOp depthFailOp; + VkCompareOp compareOp; + uint32_t compareMask; + uint32_t writeMask; + uint32_t reference; +} VkStencilOpState; + +typedef struct VkPipelineDepthStencilStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineDepthStencilStateCreateFlags flags; + VkBool32 depthTestEnable; + VkBool32 depthWriteEnable; + VkCompareOp depthCompareOp; + VkBool32 depthBoundsTestEnable; + VkBool32 stencilTestEnable; + VkStencilOpState front; + VkStencilOpState back; + float minDepthBounds; + float maxDepthBounds; +} VkPipelineDepthStencilStateCreateInfo; + +typedef struct VkPipelineColorBlendAttachmentState { + VkBool32 blendEnable; + VkBlendFactor srcColorBlendFactor; + VkBlendFactor dstColorBlendFactor; + VkBlendOp colorBlendOp; + VkBlendFactor srcAlphaBlendFactor; + VkBlendFactor dstAlphaBlendFactor; + VkBlendOp alphaBlendOp; + VkColorComponentFlags colorWriteMask; +} VkPipelineColorBlendAttachmentState; + +typedef struct VkPipelineColorBlendStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineColorBlendStateCreateFlags flags; + VkBool32 logicOpEnable; + VkLogicOp logicOp; + uint32_t attachmentCount; + const VkPipelineColorBlendAttachmentState* pAttachments; + float blendConstants[4]; +} VkPipelineColorBlendStateCreateInfo; + +typedef struct VkPipelineDynamicStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineDynamicStateCreateFlags flags; + uint32_t dynamicStateCount; + const VkDynamicState* pDynamicStates; +} VkPipelineDynamicStateCreateInfo; + +typedef struct VkGraphicsPipelineCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + const VkPipelineVertexInputStateCreateInfo* pVertexInputState; + const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState; + const VkPipelineTessellationStateCreateInfo* pTessellationState; + const VkPipelineViewportStateCreateInfo* pViewportState; + const VkPipelineRasterizationStateCreateInfo* pRasterizationState; + const VkPipelineMultisampleStateCreateInfo* pMultisampleState; + const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState; + const VkPipelineColorBlendStateCreateInfo* pColorBlendState; + const VkPipelineDynamicStateCreateInfo* pDynamicState; + VkPipelineLayout layout; + VkRenderPass renderPass; + uint32_t subpass; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkGraphicsPipelineCreateInfo; + +typedef struct VkComputePipelineCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + VkPipelineShaderStageCreateInfo stage; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkComputePipelineCreateInfo; + +typedef struct VkPushConstantRange { + VkShaderStageFlags stageFlags; + uint32_t offset; + uint32_t size; +} VkPushConstantRange; + +typedef struct VkPipelineLayoutCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineLayoutCreateFlags flags; + uint32_t setLayoutCount; + const VkDescriptorSetLayout* pSetLayouts; + uint32_t pushConstantRangeCount; + const VkPushConstantRange* pPushConstantRanges; +} VkPipelineLayoutCreateInfo; + +typedef struct VkSamplerCreateInfo { + VkStructureType sType; + const void* pNext; + VkSamplerCreateFlags flags; + VkFilter magFilter; + VkFilter minFilter; + VkSamplerMipmapMode mipmapMode; + VkSamplerAddressMode addressModeU; + VkSamplerAddressMode addressModeV; + VkSamplerAddressMode addressModeW; + float mipLodBias; + VkBool32 anisotropyEnable; + float maxAnisotropy; + VkBool32 compareEnable; + VkCompareOp compareOp; + float minLod; + float maxLod; + VkBorderColor borderColor; + VkBool32 unnormalizedCoordinates; +} VkSamplerCreateInfo; + +typedef struct VkDescriptorSetLayoutBinding { + uint32_t binding; + VkDescriptorType descriptorType; + uint32_t descriptorCount; + VkShaderStageFlags stageFlags; + const VkSampler* pImmutableSamplers; +} VkDescriptorSetLayoutBinding; + +typedef struct VkDescriptorSetLayoutCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorSetLayoutCreateFlags flags; + uint32_t bindingCount; + const VkDescriptorSetLayoutBinding* pBindings; +} VkDescriptorSetLayoutCreateInfo; + +typedef struct VkDescriptorPoolSize { + VkDescriptorType type; + uint32_t descriptorCount; +} VkDescriptorPoolSize; + +typedef struct VkDescriptorPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorPoolCreateFlags flags; + uint32_t maxSets; + uint32_t poolSizeCount; + const VkDescriptorPoolSize* pPoolSizes; +} VkDescriptorPoolCreateInfo; + +typedef struct VkDescriptorSetAllocateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorPool descriptorPool; + uint32_t descriptorSetCount; + const VkDescriptorSetLayout* pSetLayouts; +} VkDescriptorSetAllocateInfo; + +typedef struct VkDescriptorImageInfo { + VkSampler sampler; + VkImageView imageView; + VkImageLayout imageLayout; +} VkDescriptorImageInfo; + +typedef struct VkDescriptorBufferInfo { + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize range; +} VkDescriptorBufferInfo; + +typedef struct VkWriteDescriptorSet { + VkStructureType sType; + const void* pNext; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + VkDescriptorType descriptorType; + const VkDescriptorImageInfo* pImageInfo; + const VkDescriptorBufferInfo* pBufferInfo; + const VkBufferView* pTexelBufferView; +} VkWriteDescriptorSet; + +typedef struct VkCopyDescriptorSet { + VkStructureType sType; + const void* pNext; + VkDescriptorSet srcSet; + uint32_t srcBinding; + uint32_t srcArrayElement; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; +} VkCopyDescriptorSet; + +typedef struct VkFramebufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkFramebufferCreateFlags flags; + VkRenderPass renderPass; + uint32_t attachmentCount; + const VkImageView* pAttachments; + uint32_t width; + uint32_t height; + uint32_t layers; +} VkFramebufferCreateInfo; + +typedef struct VkAttachmentDescription { + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription; + +typedef struct VkAttachmentReference { + uint32_t attachment; + VkImageLayout layout; +} VkAttachmentReference; + +typedef struct VkSubpassDescription { + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t inputAttachmentCount; + const VkAttachmentReference* pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference* pColorAttachments; + const VkAttachmentReference* pResolveAttachments; + const VkAttachmentReference* pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t* pPreserveAttachments; +} VkSubpassDescription; + +typedef struct VkSubpassDependency { + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; +} VkSubpassDependency; + +typedef struct VkRenderPassCreateInfo { + VkStructureType sType; + const void* pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription* pAttachments; + uint32_t subpassCount; + const VkSubpassDescription* pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency* pDependencies; +} VkRenderPassCreateInfo; + +typedef struct VkCommandPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkCommandPoolCreateFlags flags; + uint32_t queueFamilyIndex; +} VkCommandPoolCreateInfo; + +typedef struct VkCommandBufferAllocateInfo { + VkStructureType sType; + const void* pNext; + VkCommandPool commandPool; + VkCommandBufferLevel level; + uint32_t commandBufferCount; +} VkCommandBufferAllocateInfo; + +typedef struct VkCommandBufferInheritanceInfo { + VkStructureType sType; + const void* pNext; + VkRenderPass renderPass; + uint32_t subpass; + VkFramebuffer framebuffer; + VkBool32 occlusionQueryEnable; + VkQueryControlFlags queryFlags; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkCommandBufferInheritanceInfo; + +typedef struct VkCommandBufferBeginInfo { + VkStructureType sType; + const void* pNext; + VkCommandBufferUsageFlags flags; + const VkCommandBufferInheritanceInfo* pInheritanceInfo; +} VkCommandBufferBeginInfo; + +typedef struct VkBufferCopy { + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy; + +typedef struct VkImageSubresourceLayers { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceLayers; + +typedef struct VkImageCopy { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy; + +typedef struct VkImageBlit { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit; + +typedef struct VkBufferImageCopy { + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy; + +typedef union VkClearColorValue { + float float32[4]; + int32_t int32[4]; + uint32_t uint32[4]; +} VkClearColorValue; + +typedef struct VkClearDepthStencilValue { + float depth; + uint32_t stencil; +} VkClearDepthStencilValue; + +typedef union VkClearValue { + VkClearColorValue color; + VkClearDepthStencilValue depthStencil; +} VkClearValue; + +typedef struct VkClearAttachment { + VkImageAspectFlags aspectMask; + uint32_t colorAttachment; + VkClearValue clearValue; +} VkClearAttachment; + +typedef struct VkClearRect { + VkRect2D rect; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkClearRect; + +typedef struct VkImageResolve { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve; + +typedef struct VkMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; +} VkMemoryBarrier; + +typedef struct VkBufferMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier; + +typedef struct VkImageMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier; + +typedef struct VkRenderPassBeginInfo { + VkStructureType sType; + const void* pNext; + VkRenderPass renderPass; + VkFramebuffer framebuffer; + VkRect2D renderArea; + uint32_t clearValueCount; + const VkClearValue* pClearValues; +} VkRenderPassBeginInfo; + +typedef struct VkDispatchIndirectCommand { + uint32_t x; + uint32_t y; + uint32_t z; +} VkDispatchIndirectCommand; + +typedef struct VkDrawIndexedIndirectCommand { + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; +} VkDrawIndexedIndirectCommand; + +typedef struct VkDrawIndirectCommand { + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; +} VkDrawIndirectCommand; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); +typedef void (VKAPI_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties); +typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName); +typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char* pName); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); +typedef void (VKAPI_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t* pPropertyCount, VkLayerProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); +typedef VkResult (VKAPI_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory); +typedef void (VKAPI_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData); +typedef void (VKAPI_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); +typedef VkResult (VKAPI_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); +typedef VkResult (VKAPI_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); +typedef void (VKAPI_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes); +typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef void (VKAPI_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences); +typedef VkResult (VKAPI_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout); +typedef VkResult (VKAPI_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore); +typedef void (VKAPI_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent); +typedef void (VKAPI_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool); +typedef void (VKAPI_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer); +typedef void (VKAPI_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView); +typedef void (VKAPI_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage); +typedef void (VKAPI_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout); +typedef VkResult (VKAPI_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView); +typedef void (VKAPI_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule); +typedef void (VKAPI_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache); +typedef void (VKAPI_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData); +typedef VkResult (VKAPI_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); +typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef VkResult (VKAPI_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef void (VKAPI_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler); +typedef void (VKAPI_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets); +typedef VkResult (VKAPI_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets); +typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies); +typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer); +typedef void (VKAPI_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); +typedef void (VKAPI_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity); +typedef VkResult (VKAPI_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool); +typedef void (VKAPI_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); +typedef void (VKAPI_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); +typedef VkResult (VKAPI_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo); +typedef VkResult (VKAPI_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); +typedef VkResult (VKAPI_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); +typedef void (VKAPI_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); +typedef void (VKAPI_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); +typedef void (VKAPI_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants[4]); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); +typedef void (VKAPI_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); +typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z); +typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); +typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); +typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); +typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); +typedef void (VKAPI_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects); +typedef void (VKAPI_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); +typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); +typedef void (VKAPI_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); +typedef void (VKAPI_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); +typedef void (VKAPI_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents); +typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance( + const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance); + +VKAPI_ATTR void VKAPI_CALL vkDestroyInstance( + VkInstance instance, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( + VkInstance instance, + uint32_t* pPhysicalDeviceCount, + VkPhysicalDevice* pPhysicalDevices); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures* pFeatures); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties* pFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkImageFormatProperties* pImageFormatProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties* pQueueFamilyProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties* pMemoryProperties); + +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr( + VkInstance instance, + const char* pName); + +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr( + VkDevice device, + const char* pName); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDevice( + VkDevice device, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties( + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties( + VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue( + VkDevice device, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + VkQueue* pQueue); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle( + VkQueue queue); + +VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle( + VkDevice device); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory( + VkDevice device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator, + VkDeviceMemory* pMemory); + +VKAPI_ATTR void VKAPI_CALL vkFreeMemory( + VkDevice device, + VkDeviceMemory memory, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize offset, + VkDeviceSize size, + VkMemoryMapFlags flags, + void** ppData); + +VKAPI_ATTR void VKAPI_CALL vkUnmapMemory( + VkDevice device, + VkDeviceMemory memory); + +VKAPI_ATTR VkResult VKAPI_CALL vkFlushMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges); + +VKAPI_ATTR VkResult VKAPI_CALL vkInvalidateMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceMemoryCommitment( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize* pCommittedMemoryInBytes); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory( + VkDevice device, + VkBuffer buffer, + VkDeviceMemory memory, + VkDeviceSize memoryOffset); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory( + VkDevice device, + VkImage image, + VkDeviceMemory memory, + VkDeviceSize memoryOffset); + +VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements( + VkDevice device, + VkBuffer buffer, + VkMemoryRequirements* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements( + VkDevice device, + VkImage image, + VkMemoryRequirements* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements( + VkDevice device, + VkImage image, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements* pSparseMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkSampleCountFlagBits samples, + VkImageUsageFlags usage, + VkImageTiling tiling, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueBindSparse( + VkQueue queue, + uint32_t bindInfoCount, + const VkBindSparseInfo* pBindInfo, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateFence( + VkDevice device, + const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR void VKAPI_CALL vkDestroyFence( + VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus( + VkDevice device, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore( + VkDevice device, + const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSemaphore* pSemaphore); + +VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore( + VkDevice device, + VkSemaphore semaphore, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateEvent( + VkDevice device, + const VkEventCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkEvent* pEvent); + +VKAPI_ATTR void VKAPI_CALL vkDestroyEvent( + VkDevice device, + VkEvent event, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetEventStatus( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetEvent( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetEvent( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateQueryPool( + VkDevice device, + const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkQueryPool* pQueryPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyQueryPool( + VkDevice device, + VkQueryPool queryPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetQueryPoolResults( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer( + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBuffer( + VkDevice device, + VkBuffer buffer, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferView( + VkDevice device, + const VkBufferViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBufferView* pView); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBufferView( + VkDevice device, + VkBufferView bufferView, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage( + VkDevice device, + const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImage* pImage); + +VKAPI_ATTR void VKAPI_CALL vkDestroyImage( + VkDevice device, + VkImage image, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout( + VkDevice device, + VkImage image, + const VkImageSubresource* pSubresource, + VkSubresourceLayout* pLayout); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView( + VkDevice device, + const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImageView* pView); + +VKAPI_ATTR void VKAPI_CALL vkDestroyImageView( + VkDevice device, + VkImageView imageView, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateShaderModule( + VkDevice device, + const VkShaderModuleCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkShaderModule* pShaderModule); + +VKAPI_ATTR void VKAPI_CALL vkDestroyShaderModule( + VkDevice device, + VkShaderModule shaderModule, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache( + VkDevice device, + const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineCache* pPipelineCache); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineCache( + VkDevice device, + VkPipelineCache pipelineCache, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineCacheData( + VkDevice device, + VkPipelineCache pipelineCache, + size_t* pDataSize, + void* pData); + +VKAPI_ATTR VkResult VKAPI_CALL vkMergePipelineCaches( + VkDevice device, + VkPipelineCache dstCache, + uint32_t srcCacheCount, + const VkPipelineCache* pSrcCaches); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateGraphicsPipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkGraphicsPipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateComputePipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkComputePipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipeline( + VkDevice device, + VkPipeline pipeline, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout( + VkDevice device, + const VkPipelineLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineLayout* pPipelineLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineLayout( + VkDevice device, + VkPipelineLayout pipelineLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler( + VkDevice device, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSampler* pSampler); + +VKAPI_ATTR void VKAPI_CALL vkDestroySampler( + VkDevice device, + VkSampler sampler, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout( + VkDevice device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorSetLayout* pSetLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout( + VkDevice device, + VkDescriptorSetLayout descriptorSetLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool( + VkDevice device, + const VkDescriptorPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorPool* pDescriptorPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + VkDescriptorPoolResetFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets( + VkDevice device, + const VkDescriptorSetAllocateInfo* pAllocateInfo, + VkDescriptorSet* pDescriptorSets); + +VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets( + VkDevice device, + VkDescriptorPool descriptorPool, + uint32_t descriptorSetCount, + const VkDescriptorSet* pDescriptorSets); + +VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets( + VkDevice device, + uint32_t descriptorWriteCount, + const VkWriteDescriptorSet* pDescriptorWrites, + uint32_t descriptorCopyCount, + const VkCopyDescriptorSet* pDescriptorCopies); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer( + VkDevice device, + const VkFramebufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFramebuffer* pFramebuffer); + +VKAPI_ATTR void VKAPI_CALL vkDestroyFramebuffer( + VkDevice device, + VkFramebuffer framebuffer, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass( + VkDevice device, + const VkRenderPassCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass); + +VKAPI_ATTR void VKAPI_CALL vkDestroyRenderPass( + VkDevice device, + VkRenderPass renderPass, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetRenderAreaGranularity( + VkDevice device, + VkRenderPass renderPass, + VkExtent2D* pGranularity); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool( + VkDevice device, + const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCommandPool* pCommandPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool( + VkDevice device, + VkCommandPool commandPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolResetFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers); + +VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers( + VkDevice device, + VkCommandPool commandPool, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers); + +VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer( + VkCommandBuffer commandBuffer, + const VkCommandBufferBeginInfo* pBeginInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer( + VkCommandBuffer commandBuffer, + VkCommandBufferResetFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewport( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetScissor( + VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetLineWidth( + VkCommandBuffer commandBuffer, + float lineWidth); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBias( + VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetBlendConstants( + VkCommandBuffer commandBuffer, + const float blendConstants[4]); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBounds( + VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilCompareMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t compareMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilWriteMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t writeMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilReference( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t reference); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindDescriptorSets( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t descriptorSetCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindIndexBuffer( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdDraw( + VkCommandBuffer commandBuffer, + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexed( + VkCommandBuffer commandBuffer, + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatch( + VkCommandBuffer commandBuffer, + uint32_t x, + uint32_t y, + uint32_t z); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdUpdateBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearColorValue* pColor, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearDepthStencilImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearDepthStencilValue* pDepthStencil, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearAttachments( + VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects); + +VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents( + VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent* pEvents, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + +VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetQueryPool( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp( + VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyQueryPoolResults( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize stride, + VkQueryResultFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants( + VkCommandBuffer commandBuffer, + VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents); + +VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass( + VkCommandBuffer commandBuffer, + VkSubpassContents contents); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdExecuteCommands( + VkCommandBuffer commandBuffer, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers); +#endif + +#define VK_KHR_surface 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) + +#define VK_KHR_SURFACE_SPEC_VERSION 25 +#define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" +#define VK_COLORSPACE_SRGB_NONLINEAR_KHR VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + + +typedef enum VkColorSpaceKHR { + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, + VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1), + VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkColorSpaceKHR; + +typedef enum VkPresentModeKHR { + VK_PRESENT_MODE_IMMEDIATE_KHR = 0, + VK_PRESENT_MODE_MAILBOX_KHR = 1, + VK_PRESENT_MODE_FIFO_KHR = 2, + VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, + VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPresentModeKHR; + + +typedef enum VkSurfaceTransformFlagBitsKHR { + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001, + VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002, + VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004, + VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080, + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, + VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSurfaceTransformFlagBitsKHR; +typedef VkFlags VkSurfaceTransformFlagsKHR; + +typedef enum VkCompositeAlphaFlagBitsKHR { + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008, + VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkCompositeAlphaFlagBitsKHR; +typedef VkFlags VkCompositeAlphaFlagsKHR; + +typedef struct VkSurfaceCapabilitiesKHR { + uint32_t minImageCount; + uint32_t maxImageCount; + VkExtent2D currentExtent; + VkExtent2D minImageExtent; + VkExtent2D maxImageExtent; + uint32_t maxImageArrayLayers; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkSurfaceTransformFlagBitsKHR currentTransform; + VkCompositeAlphaFlagsKHR supportedCompositeAlpha; + VkImageUsageFlags supportedUsageFlags; +} VkSurfaceCapabilitiesKHR; + +typedef struct VkSurfaceFormatKHR { + VkFormat format; + VkColorSpaceKHR colorSpace; +} VkSurfaceFormatKHR; + + +typedef void (VKAPI_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR( + VkInstance instance, + VkSurfaceKHR surface, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + VkSurfaceKHR surface, + VkBool32* pSupported); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormatsKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormatKHR* pSurfaceFormats); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes); +#endif + +#define VK_KHR_swapchain 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) + +#define VK_KHR_SWAPCHAIN_SPEC_VERSION 68 +#define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" + +typedef VkFlags VkSwapchainCreateFlagsKHR; + +typedef struct VkSwapchainCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkSwapchainCreateFlagsKHR flags; + VkSurfaceKHR surface; + uint32_t minImageCount; + VkFormat imageFormat; + VkColorSpaceKHR imageColorSpace; + VkExtent2D imageExtent; + uint32_t imageArrayLayers; + VkImageUsageFlags imageUsage; + VkSharingMode imageSharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + VkSurfaceTransformFlagBitsKHR preTransform; + VkCompositeAlphaFlagBitsKHR compositeAlpha; + VkPresentModeKHR presentMode; + VkBool32 clipped; + VkSwapchainKHR oldSwapchain; +} VkSwapchainCreateInfoKHR; + +typedef struct VkPresentInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + uint32_t swapchainCount; + const VkSwapchainKHR* pSwapchains; + const uint32_t* pImageIndices; + VkResult* pResults; +} VkPresentInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain); +typedef void (VKAPI_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); +typedef VkResult (VKAPI_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR( + VkDevice device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchain); + +VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR( + VkDevice device, + VkSwapchainKHR swapchain, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pSwapchainImageCount, + VkImage* pSwapchainImages); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR( + VkQueue queue, + const VkPresentInfoKHR* pPresentInfo); +#endif + +#define VK_KHR_display 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR) + +#define VK_KHR_DISPLAY_SPEC_VERSION 21 +#define VK_KHR_DISPLAY_EXTENSION_NAME "VK_KHR_display" + + +typedef enum VkDisplayPlaneAlphaFlagBitsKHR { + VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, + VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002, + VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004, + VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008, + VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkDisplayPlaneAlphaFlagBitsKHR; +typedef VkFlags VkDisplayPlaneAlphaFlagsKHR; +typedef VkFlags VkDisplayModeCreateFlagsKHR; +typedef VkFlags VkDisplaySurfaceCreateFlagsKHR; + +typedef struct VkDisplayPropertiesKHR { + VkDisplayKHR display; + const char* displayName; + VkExtent2D physicalDimensions; + VkExtent2D physicalResolution; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkBool32 planeReorderPossible; + VkBool32 persistentContent; +} VkDisplayPropertiesKHR; + +typedef struct VkDisplayModeParametersKHR { + VkExtent2D visibleRegion; + uint32_t refreshRate; +} VkDisplayModeParametersKHR; + +typedef struct VkDisplayModePropertiesKHR { + VkDisplayModeKHR displayMode; + VkDisplayModeParametersKHR parameters; +} VkDisplayModePropertiesKHR; + +typedef struct VkDisplayModeCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkDisplayModeCreateFlagsKHR flags; + VkDisplayModeParametersKHR parameters; +} VkDisplayModeCreateInfoKHR; + +typedef struct VkDisplayPlaneCapabilitiesKHR { + VkDisplayPlaneAlphaFlagsKHR supportedAlpha; + VkOffset2D minSrcPosition; + VkOffset2D maxSrcPosition; + VkExtent2D minSrcExtent; + VkExtent2D maxSrcExtent; + VkOffset2D minDstPosition; + VkOffset2D maxDstPosition; + VkExtent2D minDstExtent; + VkExtent2D maxDstExtent; +} VkDisplayPlaneCapabilitiesKHR; + +typedef struct VkDisplayPlanePropertiesKHR { + VkDisplayKHR currentDisplay; + uint32_t currentStackIndex; +} VkDisplayPlanePropertiesKHR; + +typedef struct VkDisplaySurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkDisplaySurfaceCreateFlagsKHR flags; + VkDisplayModeKHR displayMode; + uint32_t planeIndex; + uint32_t planeStackIndex; + VkSurfaceTransformFlagBitsKHR transform; + float globalAlpha; + VkDisplayPlaneAlphaFlagBitsKHR alphaMode; + VkExtent2D imageExtent; +} VkDisplaySurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPlanePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneSupportedDisplaysKHR( + VkPhysicalDevice physicalDevice, + uint32_t planeIndex, + uint32_t* pDisplayCount, + VkDisplayKHR* pDisplays); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + uint32_t* pPropertyCount, + VkDisplayModePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayModeKHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDisplayModeKHR* pMode); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkDisplayModeKHR mode, + uint32_t planeIndex, + VkDisplayPlaneCapabilitiesKHR* pCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR( + VkInstance instance, + const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#define VK_KHR_display_swapchain 1 +#define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 9 +#define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" + +typedef struct VkDisplayPresentInfoKHR { + VkStructureType sType; + const void* pNext; + VkRect2D srcRect; + VkRect2D dstRect; + VkBool32 persistent; +} VkDisplayPresentInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSharedSwapchainsKHR)(VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR( + VkDevice device, + uint32_t swapchainCount, + const VkSwapchainCreateInfoKHR* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchains); +#endif + +#ifdef VK_USE_PLATFORM_XLIB_KHR +#define VK_KHR_xlib_surface 1 +#include + +#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 +#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface" + +typedef VkFlags VkXlibSurfaceCreateFlagsKHR; + +typedef struct VkXlibSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkXlibSurfaceCreateFlagsKHR flags; + Display* dpy; + Window window; +} VkXlibSurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR( + VkInstance instance, + const VkXlibSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + Display* dpy, + VisualID visualID); +#endif +#endif /* VK_USE_PLATFORM_XLIB_KHR */ + +#ifdef VK_USE_PLATFORM_XCB_KHR +#define VK_KHR_xcb_surface 1 +#include + +#define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 +#define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface" + +typedef VkFlags VkXcbSurfaceCreateFlagsKHR; + +typedef struct VkXcbSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkXcbSurfaceCreateFlagsKHR flags; + xcb_connection_t* connection; + xcb_window_t window; +} VkXcbSurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR( + VkInstance instance, + const VkXcbSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXcbPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + xcb_connection_t* connection, + xcb_visualid_t visual_id); +#endif +#endif /* VK_USE_PLATFORM_XCB_KHR */ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +#define VK_KHR_wayland_surface 1 +#include + +#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 5 +#define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface" + +typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; + +typedef struct VkWaylandSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkWaylandSurfaceCreateFlagsKHR flags; + struct wl_display* display; + struct wl_surface* surface; +} VkWaylandSurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateWaylandSurfaceKHR( + VkInstance instance, + const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWaylandPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + struct wl_display* display); +#endif +#endif /* VK_USE_PLATFORM_WAYLAND_KHR */ + +#ifdef VK_USE_PLATFORM_MIR_KHR +#define VK_KHR_mir_surface 1 +#include + +#define VK_KHR_MIR_SURFACE_SPEC_VERSION 4 +#define VK_KHR_MIR_SURFACE_EXTENSION_NAME "VK_KHR_mir_surface" + +typedef VkFlags VkMirSurfaceCreateFlagsKHR; + +typedef struct VkMirSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkMirSurfaceCreateFlagsKHR flags; + MirConnection* connection; + MirSurface* mirSurface; +} VkMirSurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateMirSurfaceKHR)(VkInstance instance, const VkMirSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, MirConnection* connection); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateMirSurfaceKHR( + VkInstance instance, + const VkMirSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceMirPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + MirConnection* connection); +#endif +#endif /* VK_USE_PLATFORM_MIR_KHR */ + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +#define VK_KHR_android_surface 1 +#include + +#define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6 +#define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface" + +typedef VkFlags VkAndroidSurfaceCreateFlagsKHR; + +typedef struct VkAndroidSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkAndroidSurfaceCreateFlagsKHR flags; + ANativeWindow* window; +} VkAndroidSurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateAndroidSurfaceKHR( + VkInstance instance, + const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif +#endif /* VK_USE_PLATFORM_ANDROID_KHR */ + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#define VK_KHR_win32_surface 1 +#include + +#define VK_KHR_WIN32_SURFACE_SPEC_VERSION 5 +#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface" + +typedef VkFlags VkWin32SurfaceCreateFlagsKHR; + +typedef struct VkWin32SurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkWin32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; +} VkWin32SurfaceCreateInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR( + VkInstance instance, + const VkWin32SurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex); +#endif +#endif /* VK_USE_PLATFORM_WIN32_KHR */ + +#define VK_KHR_sampler_mirror_clamp_to_edge 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" + + +#define VK_EXT_debug_report 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) + +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 4 +#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" +#define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT + + +typedef enum VkDebugReportObjectTypeEXT { + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, + VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, + VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, + VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, + VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, + VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, + VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, + VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, + VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, + VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, + VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, + VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, + VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, + VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, + VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, + VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), + VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportObjectTypeEXT; + +typedef enum VkDebugReportErrorEXT { + VK_DEBUG_REPORT_ERROR_NONE_EXT = 0, + VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1, + VK_DEBUG_REPORT_ERROR_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_ERROR_NONE_EXT, + VK_DEBUG_REPORT_ERROR_END_RANGE_EXT = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, + VK_DEBUG_REPORT_ERROR_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT - VK_DEBUG_REPORT_ERROR_NONE_EXT + 1), + VK_DEBUG_REPORT_ERROR_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportErrorEXT; + + +typedef enum VkDebugReportFlagBitsEXT { + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, + VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, + VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportFlagBitsEXT; +typedef VkFlags VkDebugReportFlagsEXT; + +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData); + + +typedef struct VkDebugReportCallbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void* pUserData; +} VkDebugReportCallbackCreateInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); +typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT( + VkInstance instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugReportCallbackEXT* pCallback); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT( + VkInstance instance, + VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( + VkInstance instance, + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage); +#endif + +#define VK_NV_glsl_shader 1 +#define VK_NV_GLSL_SHADER_SPEC_VERSION 1 +#define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" + + +#define VK_IMG_filter_cubic 1 +#define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 +#define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" + + +#define VK_AMD_rasterization_order 1 +#define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 +#define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order" + + +typedef enum VkRasterizationOrderAMD { + VK_RASTERIZATION_ORDER_STRICT_AMD = 0, + VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, + VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD, + VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD, + VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1), + VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF +} VkRasterizationOrderAMD; + +typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { + VkStructureType sType; + const void* pNext; + VkRasterizationOrderAMD rasterizationOrder; +} VkPipelineRasterizationStateRasterizationOrderAMD; + + + +#define VK_AMD_shader_trinary_minmax 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax" + + +#define VK_AMD_shader_explicit_vertex_parameter 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter" + + +#define VK_EXT_debug_marker 1 +#define VK_EXT_DEBUG_MARKER_SPEC_VERSION 3 +#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" + +typedef struct VkDebugMarkerObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + const char* pObjectName; +} VkDebugMarkerObjectNameInfoEXT; + +typedef struct VkDebugMarkerObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugMarkerObjectTagInfoEXT; + +typedef struct VkDebugMarkerMarkerInfoEXT { + VkStructureType sType; + const void* pNext; + const char* pMarkerName; + float color[4]; +} VkDebugMarkerMarkerInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo); +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT( + VkDevice device, + VkDebugMarkerObjectTagInfoEXT* pTagInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT( + VkDevice device, + VkDebugMarkerObjectNameInfoEXT* pNameInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT( + VkCommandBuffer commandBuffer, + VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( + VkCommandBuffer commandBuffer, + VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +#endif + +#define VK_AMD_gcn_shader 1 +#define VK_AMD_GCN_SHADER_SPEC_VERSION 1 +#define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" + + +#define VK_NV_dedicated_allocation 1 +#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 +#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" + +typedef struct VkDedicatedAllocationImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationImageCreateInfoNV; + +typedef struct VkDedicatedAllocationBufferCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationBufferCreateInfoNV; + +typedef struct VkDedicatedAllocationMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkImage image; + VkBuffer buffer; +} VkDedicatedAllocationMemoryAllocateInfoNV; + + + +#define VK_AMD_draw_indirect_count 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" + +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + +#define VK_AMD_negative_viewport_height 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" + + +#define VK_AMD_gpu_shader_half_float 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" + + +#define VK_AMD_shader_ballot 1 +#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1 +#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot" + + +#define VK_IMG_format_pvrtc 1 +#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 +#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" + + +#define VK_NV_external_memory_capabilities 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" + + +typedef enum VkExternalMemoryHandleTypeFlagBitsNV { + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryHandleTypeFlagBitsNV; +typedef VkFlags VkExternalMemoryHandleTypeFlagsNV; + +typedef enum VkExternalMemoryFeatureFlagBitsNV { + VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryFeatureFlagBitsNV; +typedef VkFlags VkExternalMemoryFeatureFlagsNV; + +typedef struct VkExternalImageFormatPropertiesNV { + VkImageFormatProperties imageFormatProperties; + VkExternalMemoryFeatureFlagsNV externalMemoryFeatures; + VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; + VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; +} VkExternalImageFormatPropertiesNV; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkExternalMemoryHandleTypeFlagsNV externalHandleType, + VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); +#endif + +#define VK_NV_external_memory 1 +#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" + +typedef struct VkExternalMemoryImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExternalMemoryImageCreateInfoNV; + +typedef struct VkExportMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExportMemoryAllocateInfoNV; + + + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#define VK_NV_external_memory_win32 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" + +typedef struct VkImportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleType; + HANDLE handle; +} VkImportMemoryWin32HandleInfoNV; + +typedef struct VkExportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; +} VkExportMemoryWin32HandleInfoNV; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV( + VkDevice device, + VkDeviceMemory memory, + VkExternalMemoryHandleTypeFlagsNV handleType, + HANDLE* pHandle); +#endif +#endif /* VK_USE_PLATFORM_WIN32_KHR */ + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#define VK_NV_win32_keyed_mutex 1 +#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1 +#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" + +typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t acquireCount; + const VkDeviceMemory* pAcquireSyncs; + const uint64_t* pAcquireKeys; + const uint32_t* pAcquireTimeoutMilliseconds; + uint32_t releaseCount; + const VkDeviceMemory* pReleaseSyncs; + const uint64_t* pReleaseKeys; +} VkWin32KeyedMutexAcquireReleaseInfoNV; + + +#endif /* VK_USE_PLATFORM_WIN32_KHR */ + +#define VK_EXT_validation_flags 1 +#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" + + +typedef enum VkValidationCheckEXT { + VK_VALIDATION_CHECK_ALL_EXT = 0, + VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, + VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, + VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1), + VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationCheckEXT; + +typedef struct VkValidationFlagsEXT { + VkStructureType sType; + const void* pNext; + uint32_t disabledValidationCheckCount; + VkValidationCheckEXT* pDisabledValidationChecks; +} VkValidationFlagsEXT; + + + +#define VK_NVX_device_generated_commands 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) + +#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1 +#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" + + +typedef enum VkIndirectCommandsTokenTypeNVX { + VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0, + VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1, + VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2, + VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3, + VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4, + VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5, + VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6, + VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1), + VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsTokenTypeNVX; + +typedef enum VkObjectEntryTypeNVX { + VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0, + VK_OBJECT_ENTRY_PIPELINE_NVX = 1, + VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2, + VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3, + VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4, + VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX, + VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX, + VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1), + VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryTypeNVX; + + +typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX { + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsLayoutUsageFlagBitsNVX; +typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX; + +typedef enum VkObjectEntryUsageFlagBitsNVX { + VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, + VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, + VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryUsageFlagBitsNVX; +typedef VkFlags VkObjectEntryUsageFlagsNVX; + +typedef struct VkDeviceGeneratedCommandsFeaturesNVX { + VkStructureType sType; + const void* pNext; + VkBool32 computeBindingPointSupport; +} VkDeviceGeneratedCommandsFeaturesNVX; + +typedef struct VkDeviceGeneratedCommandsLimitsNVX { + VkStructureType sType; + const void* pNext; + uint32_t maxIndirectCommandsLayoutTokenCount; + uint32_t maxObjectEntryCounts; + uint32_t minSequenceCountBufferOffsetAlignment; + uint32_t minSequenceIndexBufferOffsetAlignment; + uint32_t minCommandsTokenBufferOffsetAlignment; +} VkDeviceGeneratedCommandsLimitsNVX; + +typedef struct VkIndirectCommandsTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + VkBuffer buffer; + VkDeviceSize offset; +} VkIndirectCommandsTokenNVX; + +typedef struct VkIndirectCommandsLayoutTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + uint32_t bindingUnit; + uint32_t dynamicCount; + uint32_t divisor; +} VkIndirectCommandsLayoutTokenNVX; + +typedef struct VkIndirectCommandsLayoutCreateInfoNVX { + VkStructureType sType; + const void* pNext; + VkPipelineBindPoint pipelineBindPoint; + VkIndirectCommandsLayoutUsageFlagsNVX flags; + uint32_t tokenCount; + const VkIndirectCommandsLayoutTokenNVX* pTokens; +} VkIndirectCommandsLayoutCreateInfoNVX; + +typedef struct VkCmdProcessCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t indirectCommandsTokenCount; + const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens; + uint32_t maxSequencesCount; + VkCommandBuffer targetCommandBuffer; + VkBuffer sequencesCountBuffer; + VkDeviceSize sequencesCountOffset; + VkBuffer sequencesIndexBuffer; + VkDeviceSize sequencesIndexOffset; +} VkCmdProcessCommandsInfoNVX; + +typedef struct VkCmdReserveSpaceForCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t maxSequencesCount; +} VkCmdReserveSpaceForCommandsInfoNVX; + +typedef struct VkObjectTableCreateInfoNVX { + VkStructureType sType; + const void* pNext; + uint32_t objectCount; + const VkObjectEntryTypeNVX* pObjectEntryTypes; + const uint32_t* pObjectEntryCounts; + const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; + uint32_t maxUniformBuffersPerDescriptor; + uint32_t maxStorageBuffersPerDescriptor; + uint32_t maxStorageImagesPerDescriptor; + uint32_t maxSampledImagesPerDescriptor; + uint32_t maxPipelineLayouts; +} VkObjectTableCreateInfoNVX; + +typedef struct VkObjectTableEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; +} VkObjectTableEntryNVX; + +typedef struct VkObjectTablePipelineEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipeline pipeline; +} VkObjectTablePipelineEntryNVX; + +typedef struct VkObjectTableDescriptorSetEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; +} VkObjectTableDescriptorSetEntryNVX; + +typedef struct VkObjectTableVertexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; +} VkObjectTableVertexBufferEntryNVX; + +typedef struct VkObjectTableIndexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; +} VkObjectTableIndexBufferEntryNVX; + +typedef struct VkObjectTablePushConstantEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkShaderStageFlags stageFlags; +} VkObjectTablePushConstantEntryNVX; + + +typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); +typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); +typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); +typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX( + VkDevice device, + const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX( + VkDevice device, + VkIndirectCommandsLayoutNVX indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX( + VkDevice device, + const VkObjectTableCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkObjectTableNVX* pObjectTable); + +VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX( + VkDevice device, + VkObjectTableNVX objectTable, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectTableEntryNVX* const* ppObjectTableEntries, + const uint32_t* pObjectIndices); + +VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectEntryTypeNVX* pObjectEntryTypes, + const uint32_t* pObjectIndices); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( + VkPhysicalDevice physicalDevice, + VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, + VkDeviceGeneratedCommandsLimitsNVX* pLimits); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5ff8aa3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.2) +project(Graphite) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime) + +function(SetMSVCOutput output) + if(MSVC) + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output}) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output}) + endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES) + endif() +endfunction() + +SetMSVCOutput(${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime/bin) + +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + +find_package(Vulkan) +find_package(OpenGL) + +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(${VULKAN_FOUND}) + option(ENABLE_VULKAN "Enable Vulkan support" ON) +endif() + +if(${OPENGL_FOUND}) + option(ENABLE_OPENGL45 "Enable OpenGL 4.5 support" ON) + option(ENABLE_OPENGLES2 "Enable OpenGL ES 2 support" ON) +endif() + +option(BUILD_EDITOR "Build editor" OFF) +option(INCLUDE_LICENSES "Include licenses" ON) + +include(paths.cmake) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +add_subdirectory(3rdparty) + +set(GAME_NAME "testgame") #used only for the executable name atm +set(GAME_FOLDER "game") #the game folder used for asset lookups + +add_definitions(-DGAME_NAME="${GAME_NAME}" -DGAME_FOLDER="${GAME_FOLDER}" -DGLM_ENABLE_EXPERIMENTAL) + +set(CMAKE_CXX_STANDARD 14) #needed for some things like better auto type deduction +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,bin") +set(CMAKE_DEBUG_POSTFIX "") #stop libraries from vendor messing with our library names + +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +if(NOT EMSCRIPTEN) + if(WIN32) + add_definitions(-DWINDOWS) + set(RUNTIME_EXPORT_STRING win32) + endif() + + if(APPLE) + add_definitions(-DMACOS) + set(RUNTIME_EXPORT_STRING macos) + endif() + + if(UNIX) + add_definitions(-DLINUX) + set(RUNTIME_EXPORT_STRING linux) + endif() + + add_definitions(-DGLFW_INCLUDE_NONE) + + if(CMAKE_CXX_COMPILER_ID MATCHES "GCC") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wold-style-cast -Wno-missing-braces") + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfloat-conversion -Wextra-semi -Wdouble-promotion -Wshadow -Wno-missing-braces -DGLFW_INCLUDE_NONE") + endif() +else() + add_definitions(-DEMSCRIPTEN) + set(RUNTIME_EXPORT_STRING emscripten) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DVDEBUG) +endif() + +add_subdirectory(engine) +add_subdirectory(dist) + +if(BUILD_EDITOR) + add_subdirectory(editor) +endif() + +add_custom_target(runtime_zip + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime + COMMAND + ${CMAKE_COMMAND} -E tar "cf" "${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime-${RUNTIME_EXPORT_STRING}.zip" --format=zip + *) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d54a0b3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2016 Joshua Goins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d552f3 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Graphite + +(This is a project from December 2016-March 2017) + +A C++ game engine that uses Vulkan to render graphics. It uses Angelscript for +scripting. + +Note: This project is still under development and _subject to change_. Expect bugs! + +## Building +### Optional Dependencies +* Qt5 for the editor + * Libarchive needed for compressing and extracting archives + +### Other Requirements +* C++14 compiler + +Make sure to clone recursively as we use git submodules to pull in dependencies: + +`git clone https://github.com/invghost/Graphite.git --recursive` + +Graphite uses CMake to generate build files. It is generally recommended to create a seperate folder like `build` to store +the intermediary files in, and then you can run: + +`cmake ..` (`..` being the location of the source code) + +Then compile with the compiler of your choosing. Your fresh binaries will show up in the `bin` folder at the root of this +project. + +In order to make a `verto-.zip` usable for editor exporting, use the cmake target `runtime_zip`. + +### Windows +`libassimp.dll` or `assimp.dll` is not included. You are required to copy these over yourself. + +MSVC is the only tested compiler at the moment. You can find more information about building Graphite on windows at + `docs/building_windows.txt` + +### macOS +Graphite compiles with Apple's Clang. It is recommended to use homebrew to install the libraries +needed. + +### Linux +Graphite should compile with a recent version of GCC or Clang. + +### Editor +By default, the editor is not built automatically. You need to set the CMake option `BUILD_EDITOR`. + +_Note_: The editor only works on Linux (specifically X11) at the moment because of how the viewport works. The editor is +planned to run on other platforms in the near future. Refer to issue [#11](https://github.com/invghost/Graphite/issues/11). + +## Distribution +In order to distribute a working game, go under the `File` menu in the editor and click `Export`. A popup will show, +giving you options on how to export your game. + +Please note that in order to export your game to the platform you choose you need your desired platform's zip in the `runtimes` + folder next to your editor. + +## License +Graphite is licensed under the MIT license. _In a nutshell_ you are welcome to use it as you please (even commerically!), +as long as you provide attribution if you distribute the source code. + +For the licenses of the in-tree third party software, look in the respective library's folder under `vendor`. diff --git a/cmake/FindLibArchive.cmake b/cmake/FindLibArchive.cmake new file mode 100644 index 0000000..bbe9c22 --- /dev/null +++ b/cmake/FindLibArchive.cmake @@ -0,0 +1,68 @@ +# - Find libarchive library and headers +# The module defines the following variables: +# +# LibArchive_FOUND - true if libarchive was found +# LibArchive_INCLUDE_DIRS - include search path +# LibArchive_LIBRARIES - libraries to link +# LibArchive_VERSION - libarchive 3-component version number + +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +find_path(LibArchive_INCLUDE_DIR + NAMES archive.h + PATHS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\LibArchive;InstallPath]/include" + /usr/local/opt/libarchive/include + ) + +find_library(LibArchive_LIBRARY + NAMES archive libarchive + PATHS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\LibArchive;InstallPath]/lib" + /usr/local/opt/libarchive/lib + ) + +mark_as_advanced(LibArchive_INCLUDE_DIR LibArchive_LIBRARY) + +# Extract the version number from the header. +if(LibArchive_INCLUDE_DIR AND EXISTS "${LibArchive_INCLUDE_DIR}/archive.h") + # The version string appears in one of two known formats in the header: + # #define ARCHIVE_LIBRARY_VERSION "libarchive 2.4.12" + # #define ARCHIVE_VERSION_STRING "libarchive 2.8.4" + # Match either format. + set(_LibArchive_VERSION_REGEX "^#define[ \t]+ARCHIVE[_A-Z]+VERSION[_A-Z]*[ \t]+\"libarchive +([0-9]+)\\.([0-9]+)\\.([0-9]+)[^\"]*\".*$") + file(STRINGS "${LibArchive_INCLUDE_DIR}/archive.h" _LibArchive_VERSION_STRING LIMIT_COUNT 1 REGEX "${_LibArchive_VERSION_REGEX}") + if(_LibArchive_VERSION_STRING) + string(REGEX REPLACE "${_LibArchive_VERSION_REGEX}" "\\1.\\2.\\3" LibArchive_VERSION "${_LibArchive_VERSION_STRING}") + endif() + unset(_LibArchive_VERSION_REGEX) + unset(_LibArchive_VERSION_STRING) +endif() + +# Handle the QUIETLY and REQUIRED arguments and set LIBARCHIVE_FOUND +# to TRUE if all listed variables are TRUE. +# (Use ${CMAKE_ROOT}/Modules instead of ${CMAKE_CURRENT_LIST_DIR} because CMake +# itself includes this FindLibArchive when built with an older CMake that does +# not provide it. The older CMake also does not have CMAKE_CURRENT_LIST_DIR.) +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +find_package_handle_standard_args(LibArchive DEFAULT_MSG + LibArchive_LIBRARY LibArchive_INCLUDE_DIR + ) +set(LibArchive_FOUND ${LIBARCHIVE_FOUND}) +unset(LIBARCHIVE_FOUND) + +if(LibArchive_FOUND) + set(LibArchive_INCLUDE_DIRS ${LibArchive_INCLUDE_DIR}) + set(LibArchive_LIBRARIES ${LibArchive_LIBRARY}) +endif() diff --git a/cmake/FindVulkan.cmake b/cmake/FindVulkan.cmake new file mode 100644 index 0000000..8b647bc --- /dev/null +++ b/cmake/FindVulkan.cmake @@ -0,0 +1,85 @@ +#.rst: +# FindVulkan +# ---------- +# +# Try to find Vulkan +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` target ``Vulkan::Vulkan``, if +# Vulkan has been found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables:: +# +# Vulkan_FOUND - True if Vulkan was found +# Vulkan_INCLUDE_DIRS - include directories for Vulkan +# Vulkan_LIBRARIES - link against this library to use Vulkan +# +# The module will also define two cache variables:: +# +# Vulkan_INCLUDE_DIR - the Vulkan include directory +# Vulkan_LIBRARY - the path to the Vulkan library +# + +#============================================================================= +# Copyright 2016 Matthaeus G. Chajdas +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +if(WIN32) + find_path(Vulkan_INCLUDE_DIR + NAMES vulkan/vulkan.h + PATHS + "$ENV{VULKAN_SDK}/Include" + ) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + find_library(Vulkan_LIBRARY + NAMES vulkan-1 + PATHS + "$ENV{VULKAN_SDK}/Bin") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + find_library(Vulkan_LIBRARY + NAMES vulkan-1 + PATHS + "$ENV{VULKAN_SDK}/Bin32") + endif() +else() + find_path(Vulkan_INCLUDE_DIR + NAMES vulkan/vulkan.h + PATHS + "$ENV{VULKAN_SDK}/include") + find_library(Vulkan_LIBRARY + NAMES vulkan + PATHS + "$ENV{VULKAN_SDK}/lib") +endif() + +set(Vulkan_LIBRARIES ${Vulkan_LIBRARY}) +set(Vulkan_INCLUDE_DIRS ${Vulkan_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Vulkan + DEFAULT_MSG + Vulkan_LIBRARY Vulkan_INCLUDE_DIR) + +mark_as_advanced(Vulkan_INCLUDE_DIR Vulkan_LIBRARY) + +if(Vulkan_FOUND AND NOT TARGET Vulkan::Vulkan) + add_library(Vulkan::Vulkan UNKNOWN IMPORTED) + set_target_properties(Vulkan::Vulkan PROPERTIES + IMPORTED_LOCATION "${Vulkan_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}") +endif() diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt new file mode 100644 index 0000000..9c43196 --- /dev/null +++ b/dist/CMakeLists.txt @@ -0,0 +1,20 @@ +file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../bin/runtime/${GAME_FOLDER}) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../bin/editor/${GAME_FOLDER}) + +add_custom_target(create_zip + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND + ${CMAKE_COMMAND} -E tar "cf" "${CMAKE_CURRENT_SOURCE_DIR}/../bin/data.vpf" --format=zip + ${CMAKE_CURRENT_SOURCE_DIR}/shaders + ${CMAKE_CURRENT_SOURCE_DIR}/light.png + ${CMAKE_CURRENT_SOURCE_DIR}/camera.png) + +add_custom_target(copydist) +add_custom_command(TARGET copydist PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/../bin/data.vpf ${CMAKE_CURRENT_SOURCE_DIR}/../bin/runtime/${GAME_FOLDER}/data.vpf) +add_custom_command(TARGET copydist PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/../bin/data.vpf ${CMAKE_CURRENT_SOURCE_DIR}/../bin/editor/${GAME_FOLDER}/data.vpf) +add_dependencies(copydist create_zip) \ No newline at end of file diff --git a/dist/camera.png b/dist/camera.png new file mode 100644 index 0000000..80598d5 Binary files /dev/null and b/dist/camera.png differ diff --git a/dist/light.png b/dist/light.png new file mode 100644 index 0000000..90d7bea Binary files /dev/null and b/dist/light.png differ diff --git a/dist/shaders/atmosphere.glsl b/dist/shaders/atmosphere.glsl new file mode 100644 index 0000000..e93a344 --- /dev/null +++ b/dist/shaders/atmosphere.glsl @@ -0,0 +1,105 @@ +#define PI 3.141592 +#define iSteps 16 +#define jSteps 8 + +vec2 rsi(vec3 r0, vec3 rd, float sr) { + // ray-sphere intersection that assumes + // the sphere is centered at the origin. + // No intersection when result.x > result.y + float a = dot(rd, rd); + float b = 2.0 * dot(rd, r0); + float c = dot(r0, r0) - (sr * sr); + float d = (b*b) - 4.0*a*c; + if (d < 0.0) return vec2(1e5,-1e5); + return vec2((-b - sqrt(d))/(2.0*a), (-b + sqrt(d))/(2.0*a)); +} + +vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) { + // Normalize the sun and view directions. + pSun = normalize(pSun); + r = normalize(r); + + // Calculate the step size of the primary ray. + vec2 p = rsi(r0, r, rAtmos); + if (p.x > p.y) return vec3(0,0,0); + p.y = min(p.y, rsi(r0, r, rPlanet).x); + float iStepSize = (p.y - p.x) / float(iSteps); + + // Initialize the primary ray time. + float iTime = 0.0; + + // Initialize accumulators for Rayleigh and Mie scattering. + vec3 totalRlh = vec3(0,0,0); + vec3 totalMie = vec3(0,0,0); + + // Initialize optical depth accumulators for the primary ray. + float iOdRlh = 0.0; + float iOdMie = 0.0; + + // Calculate the Rayleigh and Mie phases. + float mu = dot(r, pSun); + float mumu = mu * mu; + float gg = g * g; + float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu); + float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + +gg)); + + // Sample the primary ray. + for (int i = 0; i < iSteps; i++) { + + // Calculate the primary ray sample position. + vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); + + // Calculate the height of the sample. + float iHeight = length(iPos) - rPlanet; + + // Calculate the optical depth of the Rayleigh and Mie scattering for this step. + float odStepRlh = exp(-iHeight / shRlh) * iStepSize; + float odStepMie = exp(-iHeight / shMie) * iStepSize; + + // Accumulate optical depth. + iOdRlh += odStepRlh; + iOdMie += odStepMie; + + // Calculate the step size of the secondary ray. + float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps); + + // Initialize the secondary ray time. + float jTime = 0.0; + + // Initialize optical depth accumulators for the secondary ray. + float jOdRlh = 0.0; + float jOdMie = 0.0; + + // Sample the secondary ray. + for (int j = 0; j < jSteps; j++) { + + // Calculate the secondary ray sample position. + vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5); + + // Calculate the height of the sample. + float jHeight = length(jPos) - rPlanet; + + // Accumulate the optical depth. + jOdRlh += exp(-jHeight / shRlh) * jStepSize; + jOdMie += exp(-jHeight / shMie) * jStepSize; + + // Increment the secondary ray time. + jTime += jStepSize; + } + + // Calculate attenuation. + vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh))); + + // Accumulate scattering. + totalRlh += odStepRlh * attn; + totalMie += odStepMie * attn; + + // Increment the primary ray time. + iTime += iStepSize; + + } + + // Calculate and return the final color. + return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie); +} \ No newline at end of file diff --git a/dist/shaders/billboard.frag b/dist/shaders/billboard.frag new file mode 100644 index 0000000..baf89af --- /dev/null +++ b/dist/shaders/billboard.frag @@ -0,0 +1,27 @@ +layout(location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; +layout (location = 1) out vec4 outBrightColor; + +layout(binding = 1) uniform UniformDebugData { + vec4 color; + vec3 dummy; + int dummy2; +} udd; + +layout(binding = 0) uniform UniformMatrixData { + mat4 view; + mat4 proj; + mat4 model; + vec4 color; +} umd; + +layout(binding = 2) uniform sampler2D tex; + +void main() +{ + if(texture(tex, inUV).a < 0.5) + discard; + + outFragColor = umd.color; +} diff --git a/dist/shaders/billboard.vert b/dist/shaders/billboard.vert new file mode 100644 index 0000000..3289c5c --- /dev/null +++ b/dist/shaders/billboard.vert @@ -0,0 +1,20 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec2 uv; + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(binding = 0) uniform UniformMatrixData { + mat4 view; + mat4 proj; + mat4 model; + vec4 color; +} umd; + +void main() { + gl_Position = umd.proj * umd.view * umd.model * vec4(inPosition, 1.0); + uv = inPosition.xy * vec2(0.5) + vec2(0.5); + uv.y *= -1; +} diff --git a/dist/shaders/blur.frag b/dist/shaders/blur.frag new file mode 100644 index 0000000..cf66f51 --- /dev/null +++ b/dist/shaders/blur.frag @@ -0,0 +1,39 @@ +layout (binding = 0) uniform sampler2D samplerColor; + +layout (binding = 1) uniform UBO +{ + float blurScale; + float blurStrength; + int horizontal; +} ubo; + +layout(location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + float weight[5]; + weight[0] = 0.227027; + weight[1] = 0.1945946; + weight[2] = 0.1216216; + weight[3] = 0.054054; + weight[4] = 0.016216; + + vec2 tex_offset = 1.0 / textureSize(samplerColor, 0) * ubo.blurScale; // gets size of single texel + vec3 result = texture(samplerColor, inUV).rgb * weight[0]; // current fragment's contribution + for(int i = 1; i < 5; ++i) + { + if (ubo.horizontal == 1) + { + result += texture(samplerColor, inUV + vec2(tex_offset.x * i, 0.0)).rgb * weight[i] * ubo.blurStrength; + result += texture(samplerColor, inUV - vec2(tex_offset.x * i, 0.0)).rgb * weight[i] * ubo.blurStrength; + } + else + { + result += texture(samplerColor, inUV + vec2(0.0, tex_offset.y * i)).rgb * weight[i] * ubo.blurStrength; + result += texture(samplerColor, inUV - vec2(0.0, tex_offset.y * i)).rgb * weight[i] * ubo.blurStrength; + } + } + outFragColor = vec4(result, 1.0); +} \ No newline at end of file diff --git a/dist/shaders/cube.geom b/dist/shaders/cube.geom new file mode 100644 index 0000000..79c716b --- /dev/null +++ b/dist/shaders/cube.geom @@ -0,0 +1,39 @@ +layout(triangles) in; +layout(triangle_strip, max_vertices = 18) out; + +layout(binding = 8) uniform UBO +{ + mat4 viewMatrices[6]; +} ubo; + +in VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + mat3 TBN; +} vs_in[]; + +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + mat3 TBN; +} vs_out; + +void main() +{ + for (int face = 0; face < 6; ++face) { + gl_Layer = face; + for (int i = 0; i < 3; ++i) { + vs_out.FragPos = vs_in[i].FragPos; + vs_out.Normal = vs_in[i].Normal; + vs_out.TexCoords = vs_in[i].TexCoords; + vs_out.TBN = vs_in[i].TBN; + + gl_Position = ubo.viewMatrices[face] * gl_in[i].gl_Position; + + EmitVertex(); + } + EndPrimitive(); + } +} \ No newline at end of file diff --git a/dist/shaders/debug.frag b/dist/shaders/debug.frag new file mode 100644 index 0000000..9ad7ad0 --- /dev/null +++ b/dist/shaders/debug.frag @@ -0,0 +1,18 @@ +layout(location = 0) out vec4 outFragColor; + +layout(location = 0) in vec3 Position; + +layout(binding = 0) uniform UniformDebugData { + mat4 projection, view, model; + vec4 pickColor; +} udd; + +layout(binding = 1) uniform samplerCube cubeSampler; + +void main() { + if(udd.pickColor.a == 0) { + outFragColor = texture(cubeSampler, Position); + } else { + outFragColor = udd.pickColor; + } +} \ No newline at end of file diff --git a/dist/shaders/debug.vert b/dist/shaders/debug.vert new file mode 100644 index 0000000..8bf2e73 --- /dev/null +++ b/dist/shaders/debug.vert @@ -0,0 +1,20 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec4 inColor; + +layout(location = 0) out vec3 outPosition; +layout(location = 1) out vec4 outColor; + +layout(binding = 0) uniform UniformDebugData { + mat4 projection, view, model; + vec4 pickColor; +} udd; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = udd.projection * udd.view * udd.model * vec4(inPosition, 1.0); + outPosition = inPosition; + outColor = inColor; +} diff --git a/dist/shaders/deferred/ambient.frag b/dist/shaders/deferred/ambient.frag new file mode 100644 index 0000000..20fb975 --- /dev/null +++ b/dist/shaders/deferred/ambient.frag @@ -0,0 +1,10 @@ +layout(location = 0) out vec4 outColor; + +layout(location = 0) in vec2 uv; + +layout(binding = 0) uniform sampler2D albedoSampler; +layout(binding = 1) uniform sampler2D materialSampler; + +void main() { + outColor = vec4(vec3(0.01) * texture(albedoSampler, uv).rgb + vec3(texture(materialSampler, uv).b), 1.0); +} diff --git a/dist/shaders/deferred/dynamic.vert b/dist/shaders/deferred/dynamic.vert new file mode 100644 index 0000000..df6911e --- /dev/null +++ b/dist/shaders/deferred/dynamic.vert @@ -0,0 +1,34 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; +layout(location = 3) in vec3 inTangent; + +layout(location = 0) out vec3 outFragPos; +layout(location = 1) out vec3 outNormal; +layout(location = 2) out vec2 outUV; +layout(location = 3) out mat3 TBN; + +layout(std140, binding = 0) uniform UniformObjectData { + mat4 projection, view, model; + vec4 albedoTint; + vec4 emission; + float metallic, roughness; +} objectData; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = objectData.projection * objectData.view * objectData.model * vec4(inPosition, 1.0); + outFragPos = vec3(objectData.model * vec4(inPosition, 1.0)); + outNormal = transpose(inverse(mat3(objectData.model))) * inNormal; + outUV = inUV; + + vec3 Bitangent = cross(inTangent, normalize(outNormal)); + + vec3 T = normalize(vec3(objectData.model * vec4(inTangent, 0.0))); + vec3 B = normalize(vec3(objectData.model * vec4(Bitangent, 0.0))); + vec3 N = normalize(vec3(objectData.model * vec4(inNormal, 0.0))); + TBN = mat3(T, B, N); +} diff --git a/dist/shaders/deferred/gbuffer.frag b/dist/shaders/deferred/gbuffer.frag new file mode 100644 index 0000000..7501100 --- /dev/null +++ b/dist/shaders/deferred/gbuffer.frag @@ -0,0 +1,60 @@ +layout(location = 0) in vec3 inFragPos; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; +layout(location = 3) in mat3 TBN; + +layout(location = 0) out vec4 outFragPos; +layout(location = 1) out vec4 outNormal; +layout(location = 2) out vec4 outAlbedo; +layout(location = 3) out vec4 outMaterial; + +layout(std140, binding = 0) uniform UniformObjectData { + mat4 projection, view, model; + vec4 albedoTint; + vec4 emission; + float metallic, roughness; +} objectData; + +layout(binding = 1) uniform sampler2D albedoSampler; +layout(binding = 2) uniform sampler2D normalSampler; +layout(binding = 3) uniform sampler2D metallicSampler; +layout(binding = 4) uniform sampler2D roughnessSampler; + +vec3 GetAlbedo() { + if(textureSize(albedoSampler, 0).x <= 1) + return objectData.albedoTint.rgb; + + return texture(albedoSampler, inUV).rgb * objectData.albedoTint.rgb; +} + +vec3 GetNormal() { + if(textureSize(normalSampler, 0).x <= 1) + return normalize(inNormal); + + vec3 normal = texture(normalSampler, inUV).rgb; + normal = 2.0 * normal - 1.0; + normal = TBN * normal; + + return normalize(normal); +} + +float GetMetallic() { + if(textureSize(metallicSampler, 0).x <= 1) + return objectData.metallic; + + return texture(metallicSampler, inUV).r; +} + +float GetRoughness() { + if(textureSize(roughnessSampler, 0).x <= 1) + return objectData.roughness; + + return texture(roughnessSampler, inUV).r; +} + +void main() { + outFragPos = vec4(inFragPos, 1.0); + outNormal = vec4(GetNormal(), 1.0); + outAlbedo = vec4(GetAlbedo(), 1.0); + outMaterial = vec4(GetRoughness(), GetMetallic(), objectData.emission.r, 1.0); +} diff --git a/dist/shaders/deferred/point.frag b/dist/shaders/deferred/point.frag new file mode 100644 index 0000000..4853a76 --- /dev/null +++ b/dist/shaders/deferred/point.frag @@ -0,0 +1,59 @@ +#include "lighting.glsl" + +layout(location = 0) out vec4 outColor; + +layout(location = 0) in vec2 uv; + +layout(std140, binding = 0) uniform UniformSceneData +{ + mat4 projection, view, model; + vec4 cameraPosition, lightPosition; + vec3 color; + float radius; + vec2 viewport; +} sceneData; + +layout(binding = 1) uniform sampler2D positionSampler; +layout(binding = 2) uniform sampler2D normalSampler; +layout(binding = 3) uniform sampler2D albedoSampler; +layout(binding = 4) uniform sampler2D materialSampler; + +vec2 CalcTexCoord() +{ + return gl_FragCoord.xy / vec2(sceneData.viewport.x, sceneData.viewport.y); +} + +void main() { + vec3 FragPos = texture(positionSampler, CalcTexCoord()).rgb; + vec3 Normal = texture(normalSampler, CalcTexCoord()).rgb; + vec3 Albedo = texture(albedoSampler, CalcTexCoord()).rgb; + float roughness = texture(materialSampler, CalcTexCoord()).r; + float metallic = texture(materialSampler, CalcTexCoord()).g; + float emission = texture(materialSampler, CalcTexCoord()).b; + + vec3 V = normalize(sceneData.cameraPosition.xyz - FragPos); + + vec3 F0 = vec3(0.04); + F0 = mix(F0, Albedo, metallic); + vec3 F = fresnelSchlickRoughness(max(dot(Normal, V), 0.0), F0, roughness); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 L = normalize(sceneData.lightPosition.xyz - FragPos); + vec3 H = normalize(V + L); + vec3 radiance = sceneData.color * (CalculateAttenuation(sceneData.lightPosition.xyz, sceneData.radius, FragPos) * sceneData.radius); + + float NDF = DistributionGGX(Normal, H, roughness); + float G = GeometrySmith(Normal, V, L, roughness); + + vec3 nominator = NDF * G * F; + float denominator = 4 * max(dot(V, Normal), 0.0) * max(dot(L, Normal), 0.0) + 0.001; + vec3 brdf = nominator / denominator; + + // add to outgoing radiance Lo + float NdotL = max(dot(Normal, L), 0.0); + + outColor = vec4(((kD * Albedo / PI + brdf) * radiance * NdotL), 1.0); +} diff --git a/dist/shaders/deferred/point.vert b/dist/shaders/deferred/point.vert new file mode 100644 index 0000000..f1081f9 --- /dev/null +++ b/dist/shaders/deferred/point.vert @@ -0,0 +1,23 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec2 outUV; + +layout(std140, binding = 0) uniform UniformSceneData +{ + mat4 projection, view, model; + vec4 cameraPosition, lightPosition; + vec3 color; + float radius; + vec2 viewport; +} sceneData; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = sceneData.projection * sceneData.view * sceneData.model * vec4(inPosition, 1.0); + outUV = (sceneData.view * sceneData.model * vec4(inPosition, 1.0)).xy; +} diff --git a/dist/shaders/deferred/static.vert b/dist/shaders/deferred/static.vert new file mode 100644 index 0000000..415a9bb --- /dev/null +++ b/dist/shaders/deferred/static.vert @@ -0,0 +1,35 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; +layout(location = 3) in vec3 inTangent; +layout(location = 4) in mat4 inModel; + +layout(location = 0) out vec3 outFragPos; +layout(location = 1) out vec3 outNormal; +layout(location = 2) out vec2 outUV; +layout(location = 3) out mat3 TBN; + +layout(std140, binding = 0) uniform UniformObjectData { + mat4 projection, view, model; + vec4 albedoTint; + vec4 emission; + float metallic, roughness; +} objectData; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = objectData.projection * objectData.view * inModel * vec4(inPosition, 1.0); + outFragPos = vec3(inModel * vec4(inPosition, 1.0)); + outNormal = transpose(inverse(mat3(inModel))) * inNormal; + outUV = inUV; + + vec3 Bitangent = cross(inTangent, normalize(outNormal)); + + vec3 T = normalize(vec3(inModel * vec4(inTangent, 0.0))); + vec3 B = normalize(vec3(inModel * vec4(Bitangent, 0.0))); + vec3 N = normalize(vec3(inModel * vec4(inNormal, 0.0))); + TBN = mat3(T, B, N); +} diff --git a/dist/shaders/forward/lighting.frag b/dist/shaders/forward/lighting.frag new file mode 100644 index 0000000..d6f536c --- /dev/null +++ b/dist/shaders/forward/lighting.frag @@ -0,0 +1,194 @@ +#include "lighting.glsl" + +in VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + mat3 TBN; +} vs_in; + +layout(location = 0) out vec4 outColor; +layout(location = 1) out vec4 brightColor; + +struct Light { + vec4 position; + vec3 color; + int type; + vec3 direction; + float radius; + mat4 lightspace; +}; + +layout(std140, binding = 0) uniform UniformSceneData { + mat4 projection, view, model; + vec4 cameraPosition; +} sceneData; + +layout(std140, binding = 1) uniform UniformMaterialData { + vec4 albedoTint; + vec3 emission; + float metallic, roughness; +} materialData; + +layout(std140, binding = 2) uniform UniformLightData { + Light lights[MAX_LIGHTS]; + int numlights; +} lightData; + +const mat4 biasMat = mat4( + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0 ); + +layout(binding = 3) uniform sampler2D albedo_tex; +layout(binding = 4) uniform sampler2D normal_tex; +layout(binding = 5) uniform sampler2D metallic_tex; +layout(binding = 6) uniform sampler2D roughness_tex; +layout(binding = 7) uniform sampler2D directional_shadow; +layout(binding = 8) uniform samplerCube point_shadow; + +float PointShadowCalculation(Light light) +{ + if(light.position.w == 0.0f) + return 1.0f; + + vec3 fragToLight = vs_in.FragPos - light.position.xyz; + float closestDepth = texture(point_shadow, fragToLight).r; + closestDepth *= 25.0; //far-plane + + float currentDepth = length(fragToLight); + + float bias = 0.05; + float shadow = currentDepth - bias > closestDepth ? 0.0 : 1.0; + + return shadow; +} + +float ShadowCalculation(Light light) +{ + if(light.position.w == 0.0f) + return 1.0f; + + vec4 fragPosLightSpace = light.lightspace * vec4(vs_in.FragPos, 1.0); + + float bias = 0.001; + + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + float closestDepth = texture(directional_shadow, projCoords.xy).r; + float currentDepth = projCoords.z; + float shadow = currentDepth - bias > closestDepth ? 0.0 : 1.0; + + return shadow; +} + +vec4 GetAlbedo() { + if(textureSize(albedo_tex, 0).x <= 1) + return vec4(materialData.albedoTint.rgb, 1.0); + + return vec4(texture(albedo_tex, vs_in.TexCoords).rgb * materialData.albedoTint.rgb, texture(albedo_tex, vs_in.TexCoords).a); +} + +vec3 GetNormal() { + if(textureSize(normal_tex, 0).x <= 1) + return normalize(vs_in.Normal); + + vec3 normal = texture(normal_tex, vs_in.TexCoords).rgb; + normal = 2.0 * normal - 1.0; + + normal = vs_in.TBN * normal; + + return normalize(normal); +} + +float GetMetallic() { + if(textureSize(metallic_tex, 0).x <= 1) + return materialData.metallic; + + return texture(metallic_tex, vs_in.TexCoords).r; +} + +float GetRoughness() { + if(textureSize(roughness_tex, 0).x <= 1) + return materialData.roughness; + + return texture(roughness_tex, vs_in.TexCoords).r; +} + +vec3 CalculatePointLight(Light light, vec3 kD, vec3 F) { + vec3 N = GetNormal(); + vec3 V = normalize(sceneData.cameraPosition.xyz - vs_in.FragPos); + + vec3 L = normalize(light.position.xyz - vs_in.FragPos); + vec3 H = normalize(V + L); + vec3 radiance = (light.color * (CalculateAttenuation(light.position.xyz, light.radius, vs_in.FragPos) * light.radius)) * PointShadowCalculation(light); + + float NDF = DistributionGGX(N, H, GetRoughness()); + float G = GeometrySmith(N, V, L, GetRoughness()); + + vec3 nominator = NDF * G * F; + float denominator = 4 * max(dot(V, N), 0.0) * max(dot(L, N), 0.0) + 0.001; + vec3 brdf = nominator / denominator; + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + + return (kD * GetAlbedo().rgb / PI + brdf) * radiance * NdotL; +} + +vec3 CalculateDirectionalLight(Light light, vec3 kD, vec3 F) { + vec3 N = GetNormal(); + vec3 V = normalize(sceneData.cameraPosition.xyz - vs_in.FragPos); + + vec3 L = normalize(-light.direction); + vec3 H = normalize(V + L); + vec3 radiance = light.color * ShadowCalculation(light); + + float NDF = DistributionGGX(N, H, GetRoughness()); + float G = GeometrySmith(N, V, L, GetRoughness()); + + vec3 nominator = NDF * G * F; + float denominator = 4 * max(dot(V, N), 0.0) * max(dot(L, N), 0.0) + 0.001; + vec3 brdf = nominator / denominator; + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + + return (kD * GetAlbedo().rgb / PI + brdf) * radiance * NdotL; +} + +void main() { + if(GetAlbedo().a < 0.1) + discard; + + vec3 V = normalize(sceneData.cameraPosition.xyz - vs_in.FragPos); + + vec3 F0 = vec3(0.04); + F0 = mix(F0, GetAlbedo().rgb, GetMetallic()); + vec3 F = fresnelSchlickRoughness(max(dot(GetNormal(), V), 0.0), F0, GetRoughness()); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - GetMetallic(); + + vec3 result = vec3(0); + for(int i = 0; i < lightData.numlights; i++) { + switch(lightData.lights[i].type) { + case 0: + result += CalculateDirectionalLight(lightData.lights[i], kD, F); + break; + case 1: + result += CalculatePointLight(lightData.lights[i], kD, F); + break; + } + } + + vec3 ambient = vec3(0.01) * GetAlbedo().rgb; + + outColor = vec4(ambient + result + materialData.emission, GetAlbedo().a); + + float brightness = dot(outColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + if(brightness > 0.9) + brightColor = vec4(outColor.rgb, 1.0); +} diff --git a/dist/shaders/forward/normal.vert b/dist/shaders/forward/normal.vert new file mode 100644 index 0000000..0c19dd2 --- /dev/null +++ b/dist/shaders/forward/normal.vert @@ -0,0 +1,36 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inTexCoords; +layout(location = 3) in vec3 inTangent; +layout(location = 4) in vec4 inBoneWeights; +layout(location = 5) in ivec4 inBoneIDs; + +layout(std140, binding = 0) uniform UniformSceneData { + mat4 projection, view, model; + vec4 cameraPosition; +} sceneData; + +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + mat3 TBN; +} vs_out; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = sceneData.projection * sceneData.view * sceneData.model * vec4(inPosition, 1.0); + vs_out.Normal = transpose(inverse(mat3(sceneData.model))) * inNormal; + vs_out.FragPos = vec3(sceneData.model * vec4(inPosition, 1.0)); + vs_out.TexCoords = inTexCoords; + + vec3 Bitangent = cross(inTangent, normalize(vs_out.Normal)); + + vec3 T = normalize(vec3(sceneData.model * vec4(inTangent, 0.0))); + vec3 B = normalize(vec3(sceneData.model * vec4(Bitangent, 0.0))); + vec3 N = normalize(vec3(sceneData.model * vec4(inNormal, 0.0))); + vs_out.TBN = mat3(T, B, N); +} diff --git a/dist/shaders/forward/static.vert b/dist/shaders/forward/static.vert new file mode 100644 index 0000000..03c030c --- /dev/null +++ b/dist/shaders/forward/static.vert @@ -0,0 +1,35 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inTexCoords; +layout(location = 3) in vec3 inTangent; +layout(location = 4) in mat4 inModel; + +layout(std140, binding = 0) uniform UniformSceneData { + mat4 projection, view, model; + vec4 cameraPosition; +} sceneData; + +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + mat3 TBN; +} vs_out; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = sceneData.projection * sceneData.view * inModel * vec4(inPosition, 1.0); + vs_out.Normal = transpose(inverse(mat3(inModel))) * inNormal; + vs_out.FragPos = vec3(inModel * vec4(inPosition, 1.0)); + vs_out.TexCoords = inTexCoords; + + vec3 Bitangent = cross(inTangent, normalize(vs_out.Normal)); + + vec3 T = normalize(vec3(inModel * vec4(inTangent, 0.0))); + vec3 B = normalize(vec3(inModel * vec4(Bitangent, 0.0))); + vec3 N = normalize(vec3(inModel * vec4(inNormal, 0.0))); + vs_out.TBN = mat3(T, B, N); +} diff --git a/dist/shaders/grid.frag b/dist/shaders/grid.frag new file mode 100644 index 0000000..f7a45f9 --- /dev/null +++ b/dist/shaders/grid.frag @@ -0,0 +1,11 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec4 inColor; + +layout(location = 0) out vec4 outColor; + +//from haikarainen's kit +void main() +{ + outColor = inColor; + outColor.a *= max(0.0, 1.0 - (length(inPosition) / 100.0)); +} \ No newline at end of file diff --git a/dist/shaders/lighting.glsl b/dist/shaders/lighting.glsl new file mode 100644 index 0000000..84e95e8 --- /dev/null +++ b/dist/shaders/lighting.glsl @@ -0,0 +1,51 @@ +const float PI = 3.14159265359; + +//ue4 falloff model +float CalculateAttenuation(vec3 lp, float lr, vec3 FragPos) { + float distance = distance(lp, FragPos); + + float saturated = pow(clamp(1 - pow(distance / lr, 4), 0.0, 1.0), 2); + + return saturated / (pow(distance, 2) + 1); +} + +//pbr functions from learnopengl.com +vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} diff --git a/dist/shaders/post.frag b/dist/shaders/post.frag new file mode 100644 index 0000000..e6db0e9 --- /dev/null +++ b/dist/shaders/post.frag @@ -0,0 +1,27 @@ +layout(location = 0) out vec4 outColor; +layout(location = 1) out float outDepth; + +layout(location = 0) in vec2 uv; + +layout(binding = 0) uniform UniformPostData { + float exposure, gamma; +} postdata; + +layout(binding = 1) uniform sampler2D texSampler; +layout(binding = 2) uniform sampler2D brightSampler; +layout(binding = 3) uniform sampler2D brightSampler2; +layout(binding = 4) uniform sampler2D brightSampler3; +layout(binding = 5) uniform sampler2D depthSampler; + +void main() { + vec3 hdrColor = texture(texSampler, uv).rgb; + + vec3 bloomColor = texture(brightSampler, uv).rgb + texture(brightSampler2, uv).rgb + texture(brightSampler3, uv).rgb; + + vec3 mapped = vec3(1.0) - exp(-hdrColor * postdata.exposure); + mapped = pow(mapped, vec3(1.0 / postdata.gamma)); + //mapped += bloomColor; //bugged? + + outColor = vec4(mapped, 1.0); + gl_FragDepth = texture(depthSampler, uv).r; +} diff --git a/dist/shaders/quad.vert b/dist/shaders/quad.vert new file mode 100644 index 0000000..1ee7144 --- /dev/null +++ b/dist/shaders/quad.vert @@ -0,0 +1,12 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec2 uv; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = vec4(inPosition, 1.0); + uv = inPosition.xy * vec2(0.5) + vec2(0.5); +} diff --git a/dist/shaders/scube.geom b/dist/shaders/scube.geom new file mode 100644 index 0000000..0993dce --- /dev/null +++ b/dist/shaders/scube.geom @@ -0,0 +1,33 @@ +layout(triangles) in; +layout(triangle_strip, max_vertices = 18) out; + +layout(binding = 8) uniform UBO +{ + mat4 viewMatrices[6]; +} ubo; + +in VS_OUT { + vec2 uv; + vec3 fragpos; +} vs_in[]; + +out VS_OUT { + vec2 uv; + vec3 fragpos; +} vs_out; + +void main() +{ + for (int face = 0; face < 6; ++face) { + gl_Layer = face; + for (int i = 0; i < 3; ++i) { + vs_out.uv = vs_in[i].uv; + vs_out.fragpos = vs_in[i].fragpos; + + gl_Position = ubo.viewMatrices[face] * gl_in[i].gl_Position; + + EmitVertex(); + } + EndPrimitive(); + } +} \ No newline at end of file diff --git a/dist/shaders/selection.frag b/dist/shaders/selection.frag new file mode 100644 index 0000000..6d7a655 --- /dev/null +++ b/dist/shaders/selection.frag @@ -0,0 +1,11 @@ +layout(location = 0) out vec4 outFragColor; + +layout(std140, binding = 0) uniform UniformDebugData { + mat4 projection, view, model; + vec4 color; +} debugData; + +void main() +{ + outFragColor = debugData.color; +} \ No newline at end of file diff --git a/dist/shaders/selection.vert b/dist/shaders/selection.vert new file mode 100644 index 0000000..2cce4e5 --- /dev/null +++ b/dist/shaders/selection.vert @@ -0,0 +1,14 @@ +layout(location = 0) in vec3 inPosition; + +layout(std140, binding = 0) uniform UniformDebugData { + mat4 projection, view, model; + vec4 color; +} debugData; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = debugData.projection * debugData.view * debugData.model * vec4(inPosition, 1.0); +} diff --git a/dist/shaders/shadow.frag b/dist/shaders/shadow.frag new file mode 100644 index 0000000..c566309 --- /dev/null +++ b/dist/shaders/shadow.frag @@ -0,0 +1,31 @@ +layout(location = 0) in vec2 uv; + +in VS_OUT { + vec2 uv; + vec3 fragpos; +} vs_in; + +layout(binding = 0) uniform UniformBufferObject { + mat4 projection, view, model; + vec4 lightPosition; + float farPlane; +} ubo; + +layout(binding = 1) uniform sampler2D texSampler; + +void main() { + if(textureSize(texSampler, 0).x > 0) { + if(texture(texSampler, vs_in.uv).a < 0.1) { + discard; + } + } + + if(ubo.lightPosition.w == 1.0f) { + float lightDistance = length(vs_in.fragpos.xyz - ubo.lightPosition.xyz); + lightDistance = lightDistance / ubo.farPlane; + + gl_FragDepth = lightDistance; + } else { + gl_FragDepth = gl_FragCoord.z; + } +} \ No newline at end of file diff --git a/dist/shaders/shadow.vert b/dist/shaders/shadow.vert new file mode 100644 index 0000000..eb0a91a --- /dev/null +++ b/dist/shaders/shadow.vert @@ -0,0 +1,23 @@ +layout(location = 0) in vec3 inPosition; +layout(location = 2) in vec2 inTexCoords; + +out VS_OUT { + vec2 uv; + vec3 fragpos; +} vs_out; + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(binding = 0) uniform UniformBufferObject { + mat4 projection, view, model; + vec4 lightPosition; + float farPlane; +} ubo; + +void main() { + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0); + vs_out.uv = inTexCoords; + vs_out.fragpos = vec3(ubo.model * vec4(inPosition, 1.0)); +} diff --git a/dist/shaders/skybox.frag b/dist/shaders/skybox.frag new file mode 100644 index 0000000..0799d91 --- /dev/null +++ b/dist/shaders/skybox.frag @@ -0,0 +1,45 @@ +#include "atmosphere.glsl" + +in vec3 ViewDirection; + +layout(std140, binding = 1) uniform UniformSceneData { + vec3 sunPosition; + bool useTexture; +} sceneData; + +layout(location = 0) out vec4 outColor; +layout(location = 1) out vec4 outBright; + +layout(binding = 2) uniform samplerCube skyboxSampler; + +void main() +{ + vec3 result; + + if(sceneData.useTexture) + { + result = texture(skyboxSampler, ViewDirection).rgb; + } + else + { + result = atmosphere( + normalize(ViewDirection), + vec3(0, 6372e3, 0), // ray origin + sceneData.sunPosition, // position of the sun + 22.0, // intensity of the sun + 6371e3, // radius of the planet in meters + 6471e3, // radius of the atmosphere in meters + vec3(5.5e-6, 13.0e-6, 22.4e-6), // Rayleigh scattering coefficient + 21e-6, // Mie scattering coefficient + 8e3, // Rayleigh scale height + 1.2e3, // Mie scale height + 0.758 // Mie preferred scattering direction + ); + } + + outColor = vec4(result, 1.0); + + float brightness = dot(outColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + if(brightness > 1.6) + outBright = vec4(outColor.rgb, 1.0); +} \ No newline at end of file diff --git a/dist/shaders/skybox.vert b/dist/shaders/skybox.vert new file mode 100644 index 0000000..39b7117 --- /dev/null +++ b/dist/shaders/skybox.vert @@ -0,0 +1,27 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec3 ViewDirection; + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(std140, binding = 0) uniform UniformMatrixData { + mat4 proj, view; +} umd; + +void main() +{ + gl_Position = vec4(inPosition.xy, 1.0, 1.0); + + mat4 rotMatrix = mat4(1, 0, 0, 0, + 0, cos(-1.57f), -sin(-1.57f), 0, + 0, sin(-1.57f), cos(-1.57f), 0, + 0, 0, 0, 1); + + mat4 invp = inverse(umd.proj); + mat3 invm = transpose(mat3(umd.view)); + vec3 unpr = (invp * gl_Position).xyz; + + ViewDirection = invm * unpr; +} \ No newline at end of file diff --git a/docs/building_windows.txt b/docs/building_windows.txt new file mode 100644 index 0000000..9a1e826 --- /dev/null +++ b/docs/building_windows.txt @@ -0,0 +1,50 @@ +This guide is written for the latest Visual Studio released of writing, which would be Visual Studio 2015. These instructions +were written while getting Graphite to compile for Windows, so they might be incomplete or broken but should give you a +general idea of how to compile. + +Compile Dependencies +----------------- + +Bullet +1. Download latest bullet from https://github.com/bulletphysics/bullet3/releases +2. Run cmake and make sure to select the correct generator + * Make sure to set the cmake flag USE_MSVC_RUNTIME_LIBRARY_DLL! +3. Open the solution +4. Once Visual Studio is done parsing the solution, build the solution + +Assimp +1. Download assimp from https://github.com/assimp/assimp/releases +2. Run cmake and make sure to select the correct generator +3. Open the solution +4. Once Visual Studio is done parsing the solution, build the solution + +GLFW3 +1. Download GLFW3 from http://www.glfw.org/download.html (64-bit windows binaries) + +GLM +1. Download glm from https://github.com/g-truc/glm/releases + +Physfs +1. Download the stable release Physfs 2.0.3 from https://icculus.org/physfs/downloads/ +2. Run cmake and make sure to select the correct generator +3. Open the solution +4. Once Visual Studio is done parsing the solution, build the solution + +Compile Graphite +----------------- + +Note: (build type) refers to the build configuration you are targeting (Debug, Release, etc). This needs to be the same as the +libraries. + +1. Find a way to download the source code. +2. Run cmake and make sure to select the correct generator. +3. Configure the cmake variables + * assimp include dir should point towards assimp root -> include + * assimp library should point towards assimp build output -> code -> (build type) -> lib file + * Bullet include dir should point towards your bullet root -> src + * BULLET_ROOT (you will have to create this yourself) and set it to bullet build output -> lib -> (build type) + * GLFW_ROOT (ditto) and set it to glfw downloaded root + * glm include dir -> glm root + * PHYSFS_INCLUDE_DIR is physfs root + * PHYSFS_LIBRARY is physfs build output -> (build type) -> physfs.lib +5. Open and build the solution \ No newline at end of file diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt new file mode 100644 index 0000000..7ecc995 --- /dev/null +++ b/editor/CMakeLists.txt @@ -0,0 +1,119 @@ +cmake_policy(SET CMP0043 NEW) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../bin/editor) + +#brew path +if(APPLE) +list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/qt5/lib/cmake) +endif() + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(LibArchive REQUIRED) + +set(SOURCE_FILES + src/inspectors/lightinspector.cpp + src/inspectors/meshrendererinspector.cpp + src/inspectors/transforminspector.cpp + src/inspectors/camerainspector.cpp + src/inspectors/materialinspector.cpp + src/inspectors/inspectorbase.cpp + src/inspectors/scriptactorinspector.cpp + src/inspectors/playerstartinspector.cpp + src/inspectors/boxcolliderinspector.cpp + src/inspectors/rigidbodyinspector.cpp + src/inspectors/modelinspector.cpp + src/inspectors/mapinspector.cpp + src/inspectors/cubemapinspector.cpp + src/inspectors/envprobeinspector.cpp + src/assetbrowser.cpp + src/entityitemmodel.cpp + src/hierarchy.cpp + src/idselector.cpp + src/inspector.cpp + src/main.cpp + src/mainwindow.cpp + src/quaternionwidget.cpp + src/vector3widget.cpp + src/viewport.cpp + src/objectselectiondialog.cpp + src/colorwidget.cpp + src/exportwindow.cpp + src/projectwindow.cpp + src/gamesettings.cpp + src/assimpimporter.cpp + src/editorstyle.cpp + src/worldsettings.cpp) + +set(INCLUDE_FILES + include/inspectors/inspectorbase.h + include/inspectors/lightinspector.h + include/inspectors/meshrendererinspector.h + include/inspectors/transforminspector.h + include/inspectors/camerainspector.h + include/inspectors/materialinspector.h + include/inspectors/scriptactorinspector.h + include/inspectors/playerstartinspector.h + include/inspectors/boxcolliderinspector.h + include/inspectors/rigidbodyinspector.h + include/inspectors/modelinspector.h + include/inspectors/mapinspector.h + include/inspectors/cubemapinspector.h + include/inspectors/envprobeinspector.h + include/assetbrowser.h + include/hierarchy.h + include/idselector.h + include/inspector.h + include/mainwindow.h + include/quaternionwidget.h + include/vector3widget.h + include/viewport.h + include/objectselectiondialog.h + include/colorwidget.h + include/exportwindow.h + include/projectwindow.h + include/gamesettings.h + include/editorstyle.h + include/worldsettings.h) + +include_directories(include) + +include_directories( + ${ECS_INCLUDE_DIR} + ${ASSETS_INCLUDE_DIR} + ${RENDERER_INCLUDE_DIR} + ${NULLRENDERER_INCLUDE_DIR} + ${CORE_INCLUDE_DIR} + ${INPUT_INCLUDE_DIR} + ${UTILITY_INCLUDE_DIR} + ${PLATFORM_INCLUDE_DIR}) + +include_directories(SYSTEM + ${LIBRARY_JSON_INCLUDE_DIR} + ${LIBRARY_GLAD_INCLUDE_DIR} + ${LIBRARY_GLSLANG_INCLUDE_DIR} + ${LIBRARY_SPIRV_INCLUDE_DIR} + ${LibArchive_INCLUDE_DIRS} + ${LIBRARY_TWM_INCLUDE_DIR}) + +add_definitions(-DGLFW_INCLUDE_NONE) + +qt5_wrap_ui(EDITOR_UI forms/mainwindow.ui) +qt5_wrap_cpp(EDITOR_SRC ${INCLUDE_FILES}) + +add_executable(Editor ${SOURCE_FILES} ${EDITOR_UI} ${EDITOR_SRC}) +add_dependencies(Editor copydist) + +target_link_libraries(Editor assimp ToolWindowManager) +include_directories(SYSTEM ${LIBRARY_GLAD_INCLUDE_DIR} ${LIBRARY_GLSLANG_INCLUDE_DIR} ${LIBRARY_SPIRV_INCLUDE_DIR}) + +if(UNIX) + target_link_libraries(Editor X11) +endif() + +target_link_libraries(Editor Core Assets ECS Platform Input ${LibArchive_LIBRARIES}) + +qt5_use_modules(Editor Core Gui Widgets) \ No newline at end of file diff --git a/editor/forms/mainwindow.ui b/editor/forms/mainwindow.ui new file mode 100644 index 0000000..3901c4e --- /dev/null +++ b/editor/forms/mainwindow.ui @@ -0,0 +1,142 @@ + + + MainWindow + + + + 0 + 0 + 1600 + 900 + + + + Editor + + + + + + + 0 + 0 + 1600 + 30 + + + + + Fi&le + + + + + + + + + + + + + Wi&ndows + + + + + + + + + Edit + + + + + + + + + + + + + + &Open + + + + + &Viewport + + + + + &Hierarchy + + + + + &Inspector + + + + + &Asset Browser + + + + + &New + + + + + &Save + + + + + &Export + + + + + &Close + + + + + &Switch Project + + + + + &Exit + + + + + &Game Settings + + + + + &World Settings + + + + + &Duplicate + + + + + &Delete + + + + + + + diff --git a/editor/include/assetbrowser.h b/editor/include/assetbrowser.h new file mode 100644 index 0000000..50e57ce --- /dev/null +++ b/editor/include/assetbrowser.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class AssetBrowser : public QFrame +{ + Q_OBJECT +public: + explicit AssetBrowser(QWidget* parent = nullptr); + ~AssetBrowser(); + + void Rebuild(); + + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + +public slots: + void folderviewCustomContextMenu(const QPoint& point); + void indexClicked(const QModelIndex& list); + +private: + QGridLayout* m_mainLayout; + QTreeView* m_foldertree; + QFileSystemModel* m_model; + + QMenu* m_directoryMenu; + + QScrollArea* m_assetScrollArea; + QFrame* m_assetContainer; + QVBoxLayout* m_assetLayout; + + QString m_selectedDirectory; + QString m_contextDirectory; + + QMenu* m_createAssetMenu, *m_generalAssetMenu; +}; \ No newline at end of file diff --git a/editor/include/colorwidget.h b/editor/include/colorwidget.h new file mode 100644 index 0000000..fa3327f --- /dev/null +++ b/editor/include/colorwidget.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +class ColorWidget : public QWidget +{ + Q_OBJECT +public: + explicit ColorWidget(glm::vec3& ref, QWidget* parent = nullptr); + +signals: + void onValueChanged(); + +private: + glm::vec3& m_reference; +}; \ No newline at end of file diff --git a/editor/include/compress.h b/editor/include/compress.h new file mode 100644 index 0000000..e92d4f1 --- /dev/null +++ b/editor/include/compress.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include + +inline void CreateArchive(std::vector paths, const std::string& destinationPath, const std::string& rootDir) +{ + archive* archive; + archive_entry *entry; + + entry = archive_entry_new(); + + archive = archive_write_new(); + + archive_write_add_filter_none(archive); + archive_write_set_format_zip(archive); + + archive_write_open_filename(archive, destinationPath.c_str()); + for(unsigned int i = 0; i < paths.size(); i++) + { + std::fstream file(paths[i].c_str(), std::ios::binary | std::ios::in); + if(file.is_open()) + { + file.seekg(0, std::ios::end); + long size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector buffer(size); + if(size == 0 || file.read(&buffer[0], size)) + { + std::string fileName = paths[i].substr(paths[i].rfind('\\') + 1); + + if(rootDir != "") + fileName = Utility::RemoveSubstrings(fileName, {rootDir + "/"}); + + archive_entry_set_pathname(entry, fileName.c_str()); + archive_entry_set_size(entry, size); + archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_set_perm(entry, 0644); + archive_write_header(archive, entry); + + archive_write_data(archive, size == 0 ? "" : &buffer[0], size); + + file.close(); + continue; + } + file.close(); + + archive_entry_clear(entry); + } + } + + archive_entry_free(entry); + + archive_write_close(archive); +#ifndef MACOS + archive_write_free(archive); //for some reason this is undefined under macos +#endif +} + +//modified from https://github.com/libarchive/libarchive/wiki/Examples#A_Complete_Extractor +inline void copy_data(archive* ar, archive* aw) +{ + const void* buff; + size_t size; + off_t offset; + + while(archive_read_data_block(ar, &buff, &size, &offset) != ARCHIVE_EOF) + archive_write_data_block(aw, buff, size, offset); +} + +inline void ExtractArchive(const std::string& archivePath, const std::string& destPath) +{ + archive* a, *ext; + archive_entry* entry; + + a = archive_read_new(); + archive_read_support_format_all(a); + archive_read_support_filter_all(a); + + ext = archive_write_disk_new(); + archive_write_disk_set_standard_lookup(ext); + + archive_read_open_filename(a, archivePath.c_str(), 16384); + while(archive_read_next_header(a, &entry) != ARCHIVE_EOF) + { + const char* currentFile = archive_entry_pathname(entry); + std::string fullOutputPath = destPath + currentFile; + + archive_entry_set_pathname(entry, fullOutputPath.c_str()); + + archive_write_header(ext, entry); + + if (archive_entry_size(entry) > 0) + copy_data(a, ext); + + archive_write_finish_entry(ext); + } + + archive_read_close(a); + archive_read_free(a); + archive_write_close(ext); + archive_write_free(ext); +} \ No newline at end of file diff --git a/editor/include/editorstyle.h b/editor/include/editorstyle.h new file mode 100644 index 0000000..6b16f7e --- /dev/null +++ b/editor/include/editorstyle.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace Colors +{ + static QColor gray(83, 83, 80); + static QColor darkerGray(80, 80, 75); +}; + +class EditorStyle : public QProxyStyle +{ + Q_OBJECT +public: + EditorStyle(); + + void polish(QPalette &palette) override; +}; \ No newline at end of file diff --git a/editor/include/entityitemmodel.h b/editor/include/entityitemmodel.h new file mode 100644 index 0000000..9f81cd4 --- /dev/null +++ b/editor/include/entityitemmodel.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +class EntityItemModel : public QAbstractListModel +{ +public: + explicit EntityItemModel(QObject* parent = nullptr); + + int rowCount(const QModelIndex& parent) const override; + QModelIndex sibling(int row, int column, const QModelIndex& idx) const override; + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; + + void SetData(std::vector entities); + std::vector GetData(); + + QModelIndex CreateIndex(int row); + +private: + Q_DISABLE_COPY(EntityItemModel) + + std::vector m_entities; +}; diff --git a/editor/include/exportwindow.h b/editor/include/exportwindow.h new file mode 100644 index 0000000..13df9f9 --- /dev/null +++ b/editor/include/exportwindow.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class ExportWindow : public QWidget +{ + Q_OBJECT +public: + ExportWindow(QWidget* parent = nullptr); +}; \ No newline at end of file diff --git a/editor/include/gamesettings.h b/editor/include/gamesettings.h new file mode 100644 index 0000000..71c4eff --- /dev/null +++ b/editor/include/gamesettings.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class GameSettings : public QWidget +{ + Q_OBJECT +public: + GameSettings(QWidget* parent = nullptr); + +private: + void LoadSettings(); + void SaveSettings(); + + std::string m_gameName, m_defaultMap; + + QStringList searchPathList; + QStringListModel* searchPathListModel; +}; \ No newline at end of file diff --git a/editor/include/global.h b/editor/include/global.h new file mode 100644 index 0000000..8492ec1 --- /dev/null +++ b/editor/include/global.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#ifdef LINUX +#define GLFW_EXPOSE_NATIVE_X11 +#elif MACOS +//until i can figure out a way to stop the Component class conflicting +//#define GLFW_EXPOSE_NATIVE_COCOA +#endif +#include +#include + +class Entity; +class Engine; + +struct GlobalState +{ + int selectedID = 0; + bool isSelectionEntity = true; + + GLFWwindow* window = nullptr; + Engine* engine = nullptr; + + EntityPool temporaryEntityState; + + std::string projectRoot; + + bool isCurrentlyPlaying = false; + + Entity* editorCamera; +}; + +inline GlobalState& GetState() +{ + static GlobalState state; + + return state; +} \ No newline at end of file diff --git a/editor/include/hierarchy.h b/editor/include/hierarchy.h new file mode 100644 index 0000000..12b7fd6 --- /dev/null +++ b/editor/include/hierarchy.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "entityitemmodel.h" + +class Hierarchy : public QFrame +{ + Q_OBJECT +public: + explicit Hierarchy(QWidget* parent = nullptr); + ~Hierarchy(); + +public slots: + void OnChange(); + void OnSelect(const QModelIndex& index); + void viewCustomContextMenu(const QPoint& point); + +private: + QListView* m_view; + QMenu* m_entityMenu, *m_otherMenu; + + Entity* m_contextEntity; +}; \ No newline at end of file diff --git a/editor/include/idselector.h b/editor/include/idselector.h new file mode 100644 index 0000000..fcb0830 --- /dev/null +++ b/editor/include/idselector.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "objectselectiondialog.h" + +class IDSelector : public QWidget +{ +public: + Q_OBJECT +public: + explicit IDSelector(ObjectSelectionType type, int& ref, QWidget* parent = nullptr); + +signals: + void onValueChanged(); + +private: + void ReloadLabel(); + + int& m_reference; + QPushButton* m_assetButton; +}; diff --git a/editor/include/inspector.h b/editor/include/inspector.h new file mode 100644 index 0000000..5ceb65d --- /dev/null +++ b/editor/include/inspector.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "inspectors/inspectorbase.h" + +class Component; +class Asset; + +class Inspector : public QFrame +{ + Q_OBJECT +public: + explicit Inspector(QWidget* parent = nullptr); + ~Inspector(); + + QFrame* GetInspector(std::type_index, Component* component); + QFrame* GetInspectorAsset(Asset* asset); + + template + void Register() + { + m_inspectors.emplace(typeid(Object), new Inspector(nullptr)); + } + +public slots: + void Rebuild(bool override = false); + +private: + int m_lastEntitySelected = 0; + + QLabel* m_label; + QVBoxLayout* m_masterlayout; + QFrame* m_itemsContainer; + QScrollArea* m_scrollArea; + + QGridLayout* m_layout2; + + QMenu* m_addComponentMenu; + QPushButton* m_addComponentButton; + + std::vector m_inspectorFrames; + std::map m_inspectors; +}; \ No newline at end of file diff --git a/editor/include/inspectors/boxcolliderinspector.h b/editor/include/inspectors/boxcolliderinspector.h new file mode 100644 index 0000000..a624bec --- /dev/null +++ b/editor/include/inspectors/boxcolliderinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class BoxColliderInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit BoxColliderInspector(BoxCollider* boxCollider, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new BoxColliderInspector(static_cast(component), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/camerainspector.h b/editor/include/inspectors/camerainspector.h new file mode 100644 index 0000000..54c9359 --- /dev/null +++ b/editor/include/inspectors/camerainspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class CameraInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit CameraInspector(Camera* camera, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new CameraInspector(static_cast(component), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/cubemapinspector.h b/editor/include/inspectors/cubemapinspector.h new file mode 100644 index 0000000..a5332c5 --- /dev/null +++ b/editor/include/inspectors/cubemapinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class CubemapInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit CubemapInspector(Cubemap* cubemap, QWidget* parent = nullptr); + + InspectorBase* Clone(Asset* asset) override + { + return new CubemapInspector(static_cast(asset), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/envprobeinspector.h b/editor/include/inspectors/envprobeinspector.h new file mode 100644 index 0000000..3810399 --- /dev/null +++ b/editor/include/inspectors/envprobeinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class EnvironmentProbeInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit EnvironmentProbeInspector(EnvironmentProbe* probe, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new EnvironmentProbeInspector(static_cast(component), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/inspectorbase.h b/editor/include/inspectors/inspectorbase.h new file mode 100644 index 0000000..9111e0d --- /dev/null +++ b/editor/include/inspectors/inspectorbase.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Component; +class Asset; + +class InspectorBase : public QFrame +{ + Q_OBJECT +public: + InspectorBase(QWidget* parent) : QFrame(parent) {} + + //TODO: make this more consolidated (maybe templates?) + void Setup(Component* component, bool showRemoveButton = true) + { + m_layout = new QGridLayout(this); + m_layout->setAlignment(Qt::AlignTop); + m_layout->setSpacing(0); + + if(showRemoveButton) + { + QLabel* label = new QLabel(Utility::GetTypeName(GetEntPool().GetType(component)).c_str()); + m_layout->addWidget(label, 0, 0); + + m_componentHandle = component; + + QPushButton* duplicateButton = new QPushButton("Duplicate Component"); + connect(duplicateButton, &QPushButton::clicked, this, &InspectorBase::DuplicateButtonClicked); + m_layout->addWidget(duplicateButton, 0, 1, Qt::AlignRight); + + QPushButton* removeButton = new QPushButton("Remove Component"); + connect(removeButton, &QPushButton::clicked, this, &InspectorBase::RemoveButtonClicked); + m_layout->addWidget(removeButton, 0, 2); + } + } + + void AddProperty(QString name, glm::vec3& ref); + void AddProperty(QString name, glm::quat& ref); + void AddProperty(QString name, float& ref, float rate = 1.0f); + void AddProperty(QString name, ObjectSelectionType type, int& ref); + void AddProperty(QString name, bool& ref); + void AddProperty(QString name, std::string& ref); + + void AddColorProperty(QString name, glm::vec3& ref); + + void AddCustomProperty(QString name, QWidget* widget); + + virtual InspectorBase* Clone(Component*) + { + return this; + } + + virtual InspectorBase* Clone(Asset*) + { + return this; + } + +signals: + void onChange(); + +protected: + QGridLayout* m_layout; + Component* m_componentHandle; + +private: + void RemoveButtonClicked(bool checked); + void DuplicateButtonClicked(bool checked); +}; diff --git a/editor/include/inspectors/lightinspector.h b/editor/include/inspectors/lightinspector.h new file mode 100644 index 0000000..c5225fe --- /dev/null +++ b/editor/include/inspectors/lightinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class LightInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit LightInspector(Light* light, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new LightInspector(static_cast(component), parentWidget()); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/mapinspector.h b/editor/include/inspectors/mapinspector.h new file mode 100644 index 0000000..718385e --- /dev/null +++ b/editor/include/inspectors/mapinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class MapInspector : public InspectorBase +{ +Q_OBJECT +public: + explicit MapInspector(Map* map, QWidget* parent = nullptr); + + InspectorBase* Clone(Asset* asset) override + { + return new MapInspector(static_cast(asset), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/materialinspector.h b/editor/include/inspectors/materialinspector.h new file mode 100644 index 0000000..ebda117 --- /dev/null +++ b/editor/include/inspectors/materialinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class MaterialInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit MaterialInspector(Material* material, QWidget* parent = nullptr); + + InspectorBase* Clone(Asset* asset) override + { + return new MaterialInspector(static_cast(asset), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/meshrendererinspector.h b/editor/include/inspectors/meshrendererinspector.h new file mode 100644 index 0000000..5890740 --- /dev/null +++ b/editor/include/inspectors/meshrendererinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class MeshRendererInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit MeshRendererInspector(MeshRenderer* meshRenderer, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new MeshRendererInspector(static_cast(component), parentWidget()); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/modelinspector.h b/editor/include/inspectors/modelinspector.h new file mode 100644 index 0000000..b3ee7e6 --- /dev/null +++ b/editor/include/inspectors/modelinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class ModelInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit ModelInspector(Model* model, QWidget* parent = nullptr); + + InspectorBase* Clone(Asset* asset) override + { + return new ModelInspector(static_cast(asset), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/playerstartinspector.h b/editor/include/inspectors/playerstartinspector.h new file mode 100644 index 0000000..8231155 --- /dev/null +++ b/editor/include/inspectors/playerstartinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class PlayerStartInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit PlayerStartInspector(PlayerStart* pstart, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new PlayerStartInspector(static_cast(component), parentWidget()); + } +}; diff --git a/editor/include/inspectors/rigidbodyinspector.h b/editor/include/inspectors/rigidbodyinspector.h new file mode 100644 index 0000000..320ac79 --- /dev/null +++ b/editor/include/inspectors/rigidbodyinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class RigidbodyInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit RigidbodyInspector(Rigidbody* rigidbody, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new RigidbodyInspector(static_cast(component), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/scriptactorinspector.h b/editor/include/inspectors/scriptactorinspector.h new file mode 100644 index 0000000..1970715 --- /dev/null +++ b/editor/include/inspectors/scriptactorinspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class ScriptActorInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit ScriptActorInspector(ScriptActor* scriptActor, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new ScriptActorInspector(static_cast(component), nullptr); + } +}; \ No newline at end of file diff --git a/editor/include/inspectors/transforminspector.h b/editor/include/inspectors/transforminspector.h new file mode 100644 index 0000000..a30ab19 --- /dev/null +++ b/editor/include/inspectors/transforminspector.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "inspectors/inspectorbase.h" + +class TransformInspector : public InspectorBase +{ + Q_OBJECT +public: + explicit TransformInspector(Transform* transform, QWidget* parent = nullptr); + + InspectorBase* Clone(Component* component) override + { + return new TransformInspector(static_cast(component), parentWidget()); + } +}; \ No newline at end of file diff --git a/editor/include/mainwindow.h b/editor/include/mainwindow.h new file mode 100644 index 0000000..ae98d5c --- /dev/null +++ b/editor/include/mainwindow.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include "viewport.h" + +namespace Ui +{ + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + bool SaveConfirmation(); + +protected: + void closeEvent(QCloseEvent* event) override; + +private slots: + void on_actionOpen_triggered(); + void on_actionExit_triggered(); + void on_actionNew_triggered(); + void on_actionClose_triggered(); + void on_actionSave_triggered(); + void on_actionExport_triggered(); + void on_actionSwitch_triggered(); + + void on_actionHierarchy_triggered(); + void on_actionInspector_triggered(); + void on_actionAssetBrowser_triggered(); + void on_actionViewport_triggered(); + + void on_actionEditGameSettings_triggered(); + void on_actionEditWorldSettings_triggered(); + void on_actionDuplicate_triggered(); + void on_actionDelete_triggered(); + +private: + void StopPlaying(); + + void RefreshWindowTitle(); + void CreateCamera(); + + Ui::MainWindow* m_ui; + + ToolWindowManager* m_manager; + Viewport* m_viewport; + + bool m_first = true; + double m_lastX = 0.0f, m_lastY = 0.0f; + float m_yaw = 0.0f, m_pitch = 0.0f; +}; \ No newline at end of file diff --git a/editor/include/objectselectiondialog.h b/editor/include/objectselectiondialog.h new file mode 100644 index 0000000..740d1ae --- /dev/null +++ b/editor/include/objectselectiondialog.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +enum ObjectSelectionType +{ + Select_Mesh, + Select_Material, + Select_Texture, + Select_Map, + Select_Entity, + Select_Cubemap +}; + +class ObjectSelectionDialog : public QDialog +{ + Q_OBJECT +public: + ObjectSelectionDialog(ObjectSelectionType type, int& ref); + +private: + void AddToList(const std::string& name, int id); + + int& m_reference; + QVBoxLayout* m_itemsLayout; + QFrame* m_itemsContainer; + QScrollArea* m_scrollArea; + + QVBoxLayout* m_layout; +}; \ No newline at end of file diff --git a/editor/include/projectwindow.h b/editor/include/projectwindow.h new file mode 100644 index 0000000..5975482 --- /dev/null +++ b/editor/include/projectwindow.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class ProjectWindow : public QWidget +{ + Q_OBJECT +public: + ProjectWindow(QWidget* parent = nullptr); + ~ProjectWindow(); +}; \ No newline at end of file diff --git a/editor/include/quaternionwidget.h b/editor/include/quaternionwidget.h new file mode 100644 index 0000000..0228e13 --- /dev/null +++ b/editor/include/quaternionwidget.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include + +class QuaternionWidget : public QWidget +{ + Q_OBJECT +public: + explicit QuaternionWidget(glm::quat& ref, QWidget* parent = nullptr); + ~QuaternionWidget(); + + void RebuildQuat(); + +signals: + void onValueChanged(); + +private: + struct + { + QDoubleSpinBox* x, *y, *z; + } m_spinBoxes; + + glm::quat& m_reference; + glm::vec3 m_eulerRotation; + QTimer* m_updateTimer; +}; diff --git a/editor/include/vector3widget.h b/editor/include/vector3widget.h new file mode 100644 index 0000000..facd1c3 --- /dev/null +++ b/editor/include/vector3widget.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +class Vector3Widget : public QWidget +{ + Q_OBJECT +public: + explicit Vector3Widget(glm::vec3& ref, QWidget* parent = nullptr); + ~Vector3Widget(); + +signals: + void onValueChanged(); + +private: + struct + { + QDoubleSpinBox* x, *y, *z; + } m_spinBoxes; + + glm::vec3& m_reference; + QTimer* m_updateTimer; +}; \ No newline at end of file diff --git a/editor/include/viewport.h b/editor/include/viewport.h new file mode 100644 index 0000000..39610de --- /dev/null +++ b/editor/include/viewport.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include + +class Viewport : public QFrame +{ + Q_OBJECT +public: + explicit Viewport(QWidget *parent = 0); + +protected: + void resizeEvent(QResizeEvent* event) override; + +private: + QVBoxLayout* m_layout; + QToolBar* m_toolbar; + + QFrame* m_viewportFrame; +}; \ No newline at end of file diff --git a/editor/include/worldsettings.h b/editor/include/worldsettings.h new file mode 100644 index 0000000..26ae62a --- /dev/null +++ b/editor/include/worldsettings.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class WorldSettings : public QDialog +{ + Q_OBJECT +public: + WorldSettings(); + +private: + QVBoxLayout* m_layout; +}; \ No newline at end of file diff --git a/editor/src/assetbrowser.cpp b/editor/src/assetbrowser.cpp new file mode 100644 index 0000000..e0afcd8 --- /dev/null +++ b/editor/src/assetbrowser.cpp @@ -0,0 +1,229 @@ +#include "assetbrowser.h" + +#include +#include +#include +#include +#include +#include + +#include "global.h" + +AssetBrowser::AssetBrowser(QWidget *parent) : QFrame(parent) +{ + setAcceptDrops(true); + setWindowTitle("Asset Browser"); + + AssetManager::onAssetListUpdate.Register(&AssetBrowser::Rebuild, this); + + m_mainLayout = new QGridLayout(this); + m_mainLayout->setColumnMinimumWidth(1, 500); + + m_model = new QFileSystemModel; + m_model->setRootPath(GetState().projectRoot.c_str()); + m_model->setFilter(QDir::Dirs | QDir::Drives | QDir::NoDotAndDotDot | QDir::AllDirs); + m_model->setReadOnly(false); + + m_foldertree = new QTreeView(); + m_foldertree->setModel(m_model); + m_foldertree->setRootIndex(m_model->index(GetState().projectRoot.c_str())); + m_foldertree->setContextMenuPolicy(Qt::CustomContextMenu); + + //hide every column except name + for(int i = 1; i < m_model->columnCount(); i++) + m_foldertree->hideColumn(i); + + m_mainLayout->addWidget(m_foldertree, 0, 0); + + connect(m_foldertree, &QTreeView::customContextMenuRequested, this, &AssetBrowser::folderviewCustomContextMenu); + connect(m_foldertree, &QTreeView::clicked, this, &AssetBrowser::indexClicked); + + m_directoryMenu = new QMenu(); + + QAction* newDirectoryAction = new QAction("New Directory"); + connect(newDirectoryAction, &QAction::triggered, [=](){ QDir().mkdir(m_contextDirectory + "/new folder"); }); + m_directoryMenu->addAction(newDirectoryAction); + + QAction* deleteDirectoryAction = new QAction("Delete Directory"); + connect(deleteDirectoryAction, &QAction::triggered, [=]() + { + if(m_contextDirectory != GetState().projectRoot.c_str()) + { + if(QMessageBox::critical(this, + "Directory Deletion Confirmation", + "Are you sure you want to delete " + m_contextDirectory, + QMessageBox::Yes, + QMessageBox::Cancel) == QMessageBox::Yes) + { + QDir dir(m_contextDirectory); + dir.removeRecursively(); + m_selectedDirectory = ""; + Rebuild(); + } + } + }); + m_directoryMenu->addAction(deleteDirectoryAction); + + m_assetScrollArea = new QScrollArea(); + m_assetScrollArea->setWidgetResizable(true); + + m_mainLayout->addWidget(m_assetScrollArea, 0, 1); + + m_assetLayout = new QVBoxLayout(); + + m_assetContainer = new QFrame(m_assetScrollArea); + m_assetContainer->setLayout(m_assetLayout); + + m_assetScrollArea->setWidget(m_assetContainer); + + m_createAssetMenu = new QMenu; + + QAction* materialAction = new QAction("Material"); + connect(materialAction, &QAction::triggered, [=]() + { + QString name = QInputDialog::getText(this, "New Material", "Name"); + + if(name != "") + AssetManager::CreateMaterial(m_selectedDirectory.toStdString() + "/" + name.toStdString() + ".vmat"); + }); + m_createAssetMenu->addAction(materialAction); + + QAction* cubemapAction = new QAction("Cubemap"); + connect(cubemapAction, &QAction::triggered, [=]() + { + QString name = QInputDialog::getText(this, "New Cubemap", "Name"); + + if(name != "") + AssetManager::CreateCubemap(m_selectedDirectory.toStdString() + "/" + name.toStdString() + ".vcube"); + }); + m_createAssetMenu->addAction(cubemapAction); + + m_generalAssetMenu = new QMenu(); + + QAction* newAssetAction = new QAction("New Asset"); + newAssetAction->setMenu(m_createAssetMenu); + m_generalAssetMenu->addAction(newAssetAction); + + QAction* importAssetAction = new QAction("Import Asset"); + connect(importAssetAction, &QAction::triggered, [=]() + { + QString filepath = QFileDialog::getOpenFileName(this, "Import Asset", "~"); + + AssetManager::ImportAsset(filepath.toStdString(), m_selectedDirectory.toStdString()); + }); + m_generalAssetMenu->addAction(importAssetAction); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &QWidget::customContextMenuRequested, [=](const QPoint& p) + { + m_generalAssetMenu->exec(mapToGlobal(p)); + }); + + Rebuild(); +} + +AssetBrowser::~AssetBrowser() +{ + AssetManager::onAssetListUpdate.Unregister(this); +} + +void AssetBrowser::Rebuild() +{ + m_foldertree->setRootIndex(m_model->index(GetState().projectRoot.c_str())); + + QLayoutItem* item; + while((item = m_assetLayout->takeAt(0)) != NULL) + { + delete item->widget(); + delete item; + } + + if(m_selectedDirectory == "") + return; + + auto assets = AssetManager::GetDirectoryAssets(m_selectedDirectory.toStdString()); + for(auto asset : assets) + { + QPushButton* button = new QPushButton(asset->GetName().c_str()); + connect(button, &QPushButton::pressed, [=]() + { + GetState().selectedID = AssetManager::GetID(asset); + GetState().isSelectionEntity = false; + }); + + button->setContextMenuPolicy(Qt::CustomContextMenu); + connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& p) + { + QMenu contextMenu; + + QAction deleteAction("Delete"); + connect(&deleteAction, &QAction::triggered, [=]() + { + AssetManager::DeleteAsset(asset); + }); + + contextMenu.addAction(&deleteAction); + + contextMenu.exec(button->mapToGlobal(p)); + }); + + m_assetLayout->addWidget(button); + } + + //if no assets are found + if(assets.size() == 0) + { + QPushButton* button = new QPushButton("Import assets by clicking here or by dragging into this window!"); + connect(button, &QPushButton::pressed, [=]() + { + QString filepath = QFileDialog::getOpenFileName(this, "Import Asset", GetState().projectRoot.c_str()); + + AssetManager::ImportAsset(filepath.toStdString(), m_selectedDirectory.toStdString()); + }); + m_assetLayout->addWidget(button); + + QPushButton* newButton = new QPushButton("Create new asset"); + newButton->setMenu(m_createAssetMenu); + m_assetLayout->addWidget(newButton); + } +} + +void AssetBrowser::folderviewCustomContextMenu(const QPoint& point) +{ + QModelIndex index = m_foldertree->indexAt(point); + + if(index.isValid()) + { + m_contextDirectory = m_model->filePath(index); + } + else + { + m_contextDirectory = GetState().projectRoot.c_str(); + } + + m_directoryMenu->exec(m_foldertree->mapToGlobal(point)); +} + +void AssetBrowser::indexClicked(const QModelIndex& index) +{ + if(index.isValid()) + { + m_selectedDirectory = m_model->filePath(index); + Rebuild(); + } +} + +void AssetBrowser::dragEnterEvent(QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void AssetBrowser::dropEvent(QDropEvent* event) +{ + for(QUrl& url : event->mimeData()->urls()) + { + QString filename = url.toLocalFile(); + + AssetManager::ImportAsset(filename.toStdString(), m_selectedDirectory.toStdString()); + } +} diff --git a/editor/src/assimpimporter.cpp b/editor/src/assimpimporter.cpp new file mode 100644 index 0000000..c74adde --- /dev/null +++ b/editor/src/assimpimporter.cpp @@ -0,0 +1,87 @@ +#include "assimpimporter.hpp" + +#include + +#include +#include +#include +#include + +ImportedModel AssimpImporter::Import(const std::string& path) +{ + ImportedModel model; + + Assimp::Importer importer; + + const aiScene* sc = importer.ReadFile(path.c_str(), aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FlipUVs | aiProcess_CalcTangentSpace); + if (sc == nullptr) + { + std::cerr << "Error while loading model: " << importer.GetErrorString() << std::endl; + return model; + } + + std::cout << "# of materials: " << sc->mNumMaterials << std::endl; + + for(unsigned int i = 0; i < sc->mNumMaterials; i++) + { + aiString matName; + sc->mMaterials[i]->Get(AI_MATKEY_NAME, matName); + + Submaterial submaterial; + submaterial.name = matName.C_Str(); + + aiColor3D difcolor; + sc->mMaterials[i]->Get(AI_MATKEY_COLOR_DIFFUSE, difcolor); + + submaterial.diffusecolor = glm::vec3(difcolor.r, difcolor.g, difcolor.b); + + aiString difpath; + sc->mMaterials[i]->GetTexture(aiTextureType_DIFFUSE, 0, &difpath); + + submaterial.diffuseTexture = difpath.C_Str(); + + model.submaterials.push_back(submaterial); + } + + std::cout << "# of meshes: " << sc->mNumMeshes << std::endl; + + for(unsigned int i = 0; i < sc->mNumMeshes; i++) + { + aiMesh* mesh = sc->mMeshes[i]; + + Submesh submesh; + submesh.materialIndex = mesh->mMaterialIndex; + + for(unsigned int v = 0; v < mesh->mNumVertices; v++) + { + Vertex vertex; + vertex.position = glm::vec3(mesh->mVertices[v].x, mesh->mVertices[v].y, mesh->mVertices[v].z); + vertex.normal = glm::vec3(mesh->mNormals[v].x, mesh->mNormals[v].y, mesh->mNormals[v].z); + + if(mesh->HasTextureCoords(0)) + { + vertex.uv = glm::vec2(mesh->mTextureCoords[0][v].x, mesh->mTextureCoords[0][v].y); + } + + if(mesh->HasTangentsAndBitangents()) + { + vertex.tangent = glm::vec3(mesh->mTangents[v].x, mesh->mTangents[v].y, mesh->mTangents[v].z); + } + + submesh.vertices.push_back(vertex); + } + + for(unsigned int e = 0; e < mesh->mNumFaces; e++) + { + const aiFace& face = mesh->mFaces[e]; + + submesh.indices.push_back(face.mIndices[0]); + submesh.indices.push_back(face.mIndices[1]); + submesh.indices.push_back(face.mIndices[2]); + } + + model.submeshes.push_back(submesh); + } + + return model; +} \ No newline at end of file diff --git a/editor/src/colorwidget.cpp b/editor/src/colorwidget.cpp new file mode 100644 index 0000000..0c4b6e5 --- /dev/null +++ b/editor/src/colorwidget.cpp @@ -0,0 +1,68 @@ +#include "colorwidget.h" + +#include +#include + +QColor FromVec3(glm::vec3 vec) +{ + int r = 0, g = 0, b = 0; + + if(vec.x != 0) + r = static_cast(255.0f * vec.x); + + if(vec.y != 0) + g = static_cast(255.0f * vec.y); + + if(vec.z != 0) + b = static_cast(255.0f * vec.z); + + return QColor::fromRgb(r, g, b); +} + +glm::vec3 FromQColor(QColor color) +{ + glm::vec3 vec; + + int r, g, b, a; + color.getRgb(&r, &g, &b, &a); + + vec.x = r / 255.0f; + vec.y = g / 255.0f; + vec.z = b / 255.0f; + + return vec; +} + +ColorWidget::ColorWidget(glm::vec3 &ref, QWidget *parent) : QWidget(parent), m_reference(ref) +{ + QHBoxLayout* itemsLayout = new QHBoxLayout(this); + + QPushButton* colorButton = new QPushButton(); + colorButton->setFlat(true); + + QPalette pal = colorButton->palette(); + pal.setColor(QPalette::Button, FromVec3(m_reference)); + + colorButton->setAutoFillBackground(true); + colorButton->setPalette(pal); + colorButton->update(); + + connect(colorButton, &QPushButton::clicked, [=](bool) + { + QColor oldcolor = FromVec3(m_reference); + + QColor newcolor = QColorDialog::getColor(oldcolor); + + m_reference = FromQColor(newcolor); + + QPalette newpal = colorButton->palette(); + newpal.setColor(QPalette::Button, newcolor); + + colorButton->setPalette(newpal); + colorButton->update(); + + emit onValueChanged(); + }); + + itemsLayout->addWidget(colorButton); +} \ No newline at end of file diff --git a/editor/src/editorstyle.cpp b/editor/src/editorstyle.cpp new file mode 100644 index 0000000..f87d144 --- /dev/null +++ b/editor/src/editorstyle.cpp @@ -0,0 +1,12 @@ +#include "editorstyle.h" + +#include + +EditorStyle::EditorStyle() : QProxyStyle(QStyleFactory::create("windows")) {} + +void EditorStyle::polish(QPalette& palette) +{ + palette = QPalette(Colors::gray); + + palette.setBrush(QPalette::Base, Colors::darkerGray); +} \ No newline at end of file diff --git a/editor/src/entityitemmodel.cpp b/editor/src/entityitemmodel.cpp new file mode 100644 index 0000000..3a21041 --- /dev/null +++ b/editor/src/entityitemmodel.cpp @@ -0,0 +1,117 @@ +#include "entityitemmodel.h" + +#include +#include +#include + +EntityItemModel::EntityItemModel(QObject* parent) : QAbstractListModel(parent) {} + +int EntityItemModel::rowCount(const QModelIndex &parent) const +{ + if(parent.isValid()) + return 0; + + return static_cast(m_entities.size()); +} + +QModelIndex EntityItemModel::sibling(int row, int column, const QModelIndex &idx) const +{ + if(!idx.isValid() || column != 0 || static_cast(row) >= m_entities.size()) + return QModelIndex(); + + return createIndex(row, 0); +} + +QVariant EntityItemModel::data(const QModelIndex &index, int role) const +{ + if(index.row() < 0 || static_cast(index.row()) >= m_entities.size()) + return QVariant(); + + if(!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole || role == Qt::EditRole) + return QString(m_entities[index.row()]->name.c_str()); + + return QVariant(); +} + +bool EntityItemModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(static_cast(index.row()) < m_entities.size() && (role == Qt::EditRole || role == Qt::DisplayRole)) + { + m_entities[index.row()]->name = value.toString().toStdString(); + + QVector roles; + roles.reserve(2); + roles.append(Qt::DisplayRole); + roles.append(Qt::EditRole); + + emit dataChanged(index, index, roles); + + return true; + } + else + { + Log::Print("unable to change %i", index.row()); + } + + return false; +} + +bool EntityItemModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (count < 1 || row < 0 || row > rowCount(parent)) + return false; + + beginInsertRows(QModelIndex(), row, row + count - 1); + + for (int r = 0; r < count; ++r) + m_entities.insert(m_entities.begin() + row, GetEntPool().CreateEntity(GetState().engine->GetGame()->GetWorld())); + + endInsertRows(); + + return true; +} + +bool EntityItemModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (count <= 0 || row < 0 || (row + count) > rowCount(parent)) + return false; + + beginRemoveRows(QModelIndex(), row, row + count - 1); + + const auto it = m_entities.begin() + row; + m_entities.erase(it, it + count); + + endRemoveRows(); + + return true; +} + +Qt::ItemFlags EntityItemModel::flags(const QModelIndex &index) const +{ + if(!index.isValid()) + return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled; + + return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +void EntityItemModel::SetData(std::vector entities) +{ + beginResetModel(); + + m_entities = entities; + + endResetModel(); +} + +std::vector EntityItemModel::GetData() +{ + return m_entities; +} + +QModelIndex EntityItemModel::CreateIndex(int row) +{ + return createIndex(row, 0); +} diff --git a/editor/src/exportwindow.cpp b/editor/src/exportwindow.cpp new file mode 100644 index 0000000..40f0643 --- /dev/null +++ b/editor/src/exportwindow.cpp @@ -0,0 +1,133 @@ +#include "exportwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "compress.h" + +struct ExportOption +{ + std::string name, pathname; +}; + +std::vector GetExportOptions() +{ + std::vector tmp; + + if(!Filesystem::DirectoryExists("runtimes")) + return tmp; + + auto contents = Filesystem::DirectoryContents("runtimes"); + for(auto file : contents) + { + ExportOption option; + + std::string first = file.substr(file.find_last_of("-") + 1, file.length()); + option.name = first.substr(0, first.find_last_of(".")); + option.pathname = file; + + Log::Print("%s: %s", option.name.c_str(), option.pathname.c_str()); + + tmp.push_back(option); + } + + return tmp; +} + +ExportWindow::ExportWindow(QWidget* parent) : QWidget(parent) +{ + setWindowTitle("Export Game"); + + QVBoxLayout* layout = new QVBoxLayout(this); + + QCheckBox* runCheckbox = new QCheckBox("Run after export"); + + auto exports = GetExportOptions(); + + QLabel* runtimesLabel = new QLabel("Available Targets:"); + layout->addWidget(runtimesLabel); + + QListView* exportList = new QListView(); + + QStringListModel* exportStringList = new QStringListModel(); + QStringList list; + for(auto ex : exports) + { + list.push_back(ex.name.c_str()); + } + + exportStringList->setStringList(list); + exportList->setModel(exportStringList); + + layout->addWidget(exportList); + + QPushButton* pushButton = new QPushButton("Export!"); + connect(pushButton, &QPushButton::clicked, [=]() + { + Filesystem::CreateDirectory("out"); + + ExtractArchive(exports[0].pathname, "out/"); + + Filesystem::CreateDirectory("out/" + std::string(GAME_FOLDER)); + //Filesystem::CopyFile("runtime/" + std::string(GAME_NAME), "out/" + std::string(GAME_NAME), true); + Filesystem::CopyFile(GetState().projectRoot + "/game.json", "out/game/game.json", true); + + Filesystem::CopyDirectory("data/shaders", "out/data/shaders"); + + std::vector paths; + + auto intermediatePaths = AssetManager::GetFilepaths(); + for(unsigned int i = 0; i < intermediatePaths.size(); i++) + { + Model* model = AssetManager::GetByPath(intermediatePaths[i]); + + //if this asset is a model + if(model != nullptr) + { + ModelImporter::Export(intermediatePaths[i], Utility::RemoveFileExtension(intermediatePaths[i]) + ".model"); + paths.push_back(Utility::RemoveFileExtension(intermediatePaths[i]) + ".model"); + } + else + { + paths.push_back(intermediatePaths[i]); + } + } + + std::ofstream out; + out.open("index.txt"); + + for(unsigned int i = 0; i < AssetManager::GetFilepaths().size(); i++) + { + std::string path = Utility::RemoveSubstrings(paths[i], { GetState().projectRoot + "/" }); + out << Utility::RemoveFileExtension(path) + ".meta" + "\n"; + paths.push_back(Utility::RemoveFileExtension(paths[i]) + ".meta"); + } + + out.close(); + + paths.push_back("index.txt"); + paths.push_back(GetState().projectRoot + "/maps/test.vmap"); + + CreateArchive(paths, "out/" + std::string(GAME_FOLDER) + "/assets.vpf", GetState().projectRoot); + + Filesystem::Remove("index.txt"); + + hide(); + + if(runCheckbox->isChecked()) + { + system(("cd out/; ./" + std::string(GAME_NAME)).c_str()); //TODO: very insecure, find a way to make this more robust and cross-platform + } + }); + + layout->addWidget(pushButton); + layout->addWidget(runCheckbox); +} diff --git a/editor/src/gamesettings.cpp b/editor/src/gamesettings.cpp new file mode 100644 index 0000000..5795f42 --- /dev/null +++ b/editor/src/gamesettings.cpp @@ -0,0 +1,134 @@ +#include "gamesettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" + +GameSettings::GameSettings(QWidget* parent) : QWidget(parent) +{ + LoadSettings(); + + QVBoxLayout* layout = new QVBoxLayout(this); + + QLabel* gameNameLabel = new QLabel("Game name"); + layout->addWidget(gameNameLabel); + + QLineEdit* gameNameEdit = new QLineEdit(); + gameNameEdit->setText(m_gameName.c_str()); + + connect(gameNameEdit, &QLineEdit::textChanged, [=](const QString& val) + { + m_gameName = val.toStdString(); + }); + layout->addWidget(gameNameEdit); + + QLabel* defaultMapLabel = new QLabel("Default map"); + layout->addWidget(defaultMapLabel); + + QLineEdit* defaultMapEdit = new QLineEdit(); + defaultMapEdit->setText(m_defaultMap.c_str()); + + connect(defaultMapEdit, &QLineEdit::textChanged, [=](const QString& val) + { + m_defaultMap = val.toStdString(); + }); + layout->addWidget(defaultMapEdit); + + QLabel* searchPathLabel = new QLabel("Asset search paths"); + layout->addWidget(searchPathLabel); + + searchPathListModel = new QStringListModel(); + searchPathListModel->setStringList(searchPathList); + + QListView* searchPathListView = new QListView(); + searchPathListView->setModel(searchPathListModel); + + layout->addWidget(searchPathListView); + + QHBoxLayout* pathButtonLayout = new QHBoxLayout(); + + QPushButton* addPathButton = new QPushButton("Add"); + connect(addPathButton, &QPushButton::clicked, [=](){ + int row = searchPathListModel->rowCount(); + + searchPathListModel->insertRows(row, 1); + + QModelIndex index = searchPathListModel->index(row); + + searchPathListView->setCurrentIndex(index); + searchPathListView->edit(index); + }); + + pathButtonLayout->addWidget(addPathButton); + + QPushButton* removePathButton = new QPushButton("Remove"); + connect(removePathButton, &QPushButton::clicked, [=](){ + int row = searchPathListModel->rowCount(); + + searchPathListModel->removeRows(row - 1, 1); + }); + + pathButtonLayout->addWidget(removePathButton); + + layout->addLayout(pathButtonLayout); + + QPushButton* saveButton = new QPushButton("Save Settings"); + connect(saveButton, &QPushButton::clicked, [=]() + { + SaveSettings(); + hide(); + }); + layout->addWidget(saveButton); +} + +void GameSettings::LoadSettings() +{ + std::ifstream in(GetState().projectRoot + "/game.json"); + if(in.is_open()) + { + nlohmann::json file; + file << in; + + m_gameName = file["game_name"]; + m_defaultMap = file["default_map"]; + + searchPathList.clear(); + + nlohmann::json search_paths = file["search_paths"]; + for(auto path : search_paths) + { + std::string str = path; + searchPathList.push_back(str.c_str()); + } + } +} + +void GameSettings::SaveSettings() +{ + nlohmann::json file; + + file["game_name"] = m_gameName; + file["default_map"] = m_defaultMap; + + searchPathList = searchPathListModel->stringList(); + + nlohmann::json& search_paths = file["search_paths"]; + for(auto path : searchPathList) + { + Log::Print("%s", path.toStdString().c_str()); + search_paths.push_back(path.toStdString()); + } + + std::ofstream out; + out.open(GetState().projectRoot + "/game.json"); + + out << file.dump(1); + out.close(); +} \ No newline at end of file diff --git a/editor/src/hierarchy.cpp b/editor/src/hierarchy.cpp new file mode 100644 index 0000000..571a641 --- /dev/null +++ b/editor/src/hierarchy.cpp @@ -0,0 +1,121 @@ +#include "hierarchy.h" + +#include +#include +#include + +#include "global.h" + +Hierarchy::Hierarchy(QWidget* parent) : QFrame(parent) +{ + setWindowTitle("Hierarchy"); + + QVBoxLayout* layout = new QVBoxLayout(this); + + m_view = new QListView(); + layout->addWidget(m_view); + m_view->setModel(new EntityItemModel); + m_view->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_view, &QListView::customContextMenuRequested, this, &Hierarchy::viewCustomContextMenu); + + GetEntPool().onEntityListChange.Register(&Hierarchy::OnChange, this); + + connect(m_view, &QListView::clicked, this, &Hierarchy::OnSelect); + + m_entityMenu = new QMenu(); + + QAction* deleteEntityAction = new QAction("Delete"); + connect(deleteEntityAction, &QAction::triggered, [=]() + { + if(GetState().engine->GetGame()->GetWorld() == nullptr) + return; + + if(m_contextEntity != nullptr && m_contextEntity->tag != "editor") + { + if(QMessageBox::critical(this, + "Entity Deletion Confirmation", "Are you sure you want to delete " + QString(m_contextEntity->name.c_str()), + QMessageBox::Yes, + QMessageBox::Cancel) == QMessageBox::Yes) + { + GetEntPool().Delete(m_contextEntity); + m_contextEntity = nullptr; + } + } + }); + m_entityMenu->addAction(deleteEntityAction); + + QAction* duplicateEntityAction = new QAction("Duplicate"); + connect(duplicateEntityAction, &QAction::triggered, [=]() + { + if(GetState().engine->GetGame()->GetWorld() == nullptr) + return; + + if(m_contextEntity != nullptr && m_contextEntity->tag != "editor") + { + GetEntPool().Duplicate(m_contextEntity, GetState().engine->GetGame()->GetWorld()); + } + }); + m_entityMenu->addAction(duplicateEntityAction); + + m_otherMenu = new QMenu(); + + QAction* newEntityAction = new QAction("New Entity"); + connect(newEntityAction, &QAction::triggered, [=]() + { + if(GetState().engine->GetGame()->GetWorld() == nullptr) + return; + + GetEntPool().CreateEntity(GetState().engine->GetGame()->GetWorld()); + }); + m_otherMenu->addAction(newEntityAction); + + GetEntPool().onEntityListChange(); //force refresh +} + +Hierarchy::~Hierarchy() +{ + GetEntPool().onEntityListChange.Unregister(this); + + delete m_otherMenu; + delete m_entityMenu; + delete m_view; + + m_otherMenu = nullptr; + m_entityMenu = nullptr; + m_view = nullptr; +} + +void Hierarchy::OnChange() +{ + auto entities = GetEntPool().GetAllEntities(); + for(unsigned int i = 0; i < entities.size(); i++) + if(entities[i]->tag == "editor") + entities.erase(entities.begin() + i); + + if(static_cast(m_view->currentIndex().row()) > entities.size()) + m_view->setCurrentIndex(static_cast(m_view->model())->CreateIndex(entities.size())); + + static_cast(m_view->model())->SetData(entities); +} + +void Hierarchy::OnSelect(const QModelIndex &index) +{ + GetState().selectedID = static_cast(m_view->model())->GetData()[index.row()]->GetID(); + GetState().isSelectionEntity = true; +} + +void Hierarchy::viewCustomContextMenu(const QPoint &point) +{ + QModelIndex index = m_view->indexAt(point); + + if(index.isValid()) + { + m_contextEntity = static_cast(m_view->model())->GetData()[index.row()]; + m_entityMenu->exec(m_view->mapToGlobal(point)); + } + else + { + m_otherMenu->exec(m_view->mapToGlobal(point)); + } +} \ No newline at end of file diff --git a/editor/src/idselector.cpp b/editor/src/idselector.cpp new file mode 100644 index 0000000..b4b57eb --- /dev/null +++ b/editor/src/idselector.cpp @@ -0,0 +1,60 @@ +#include "idselector.h" + +#include +#include +#include +#include + +#include "global.h" + +IDSelector::IDSelector(ObjectSelectionType type, int& ref, QWidget *parent) : QWidget(parent), m_reference(ref) +{ + QHBoxLayout* itemsLayout = new QHBoxLayout(this); + + m_assetButton = new QPushButton("..."); + connect(m_assetButton, &QPushButton::clicked, [=]() + { + Asset* asset = AssetManager::GetAsset(ref); + if(asset != nullptr) + { + GetState().selectedID = ref; + GetState().isSelectionEntity = false; + } + }); + + itemsLayout->addWidget(m_assetButton); + + ReloadLabel(); + + QPushButton* pushButton = new QPushButton("Select..."); + connect(pushButton, &QPushButton::pressed, [=]() + { + ObjectSelectionDialog dialog(type, m_reference); + dialog.exec(); + + emit onValueChanged(); + ReloadLabel(); + }); + itemsLayout->addWidget(pushButton); +} + +void IDSelector::ReloadLabel() +{ + Asset* asset = AssetManager::GetAsset(m_reference); + if (asset != nullptr) + { + m_assetButton->setText(asset->GetName().c_str()); + return; + } + else + { + Entity* entity = GetEntPool().GetByID(m_reference); + if(entity != nullptr) + { + m_assetButton->setText(entity->name.c_str()); + return; + } + } + + m_assetButton->setText("..."); +} \ No newline at end of file diff --git a/editor/src/inspector.cpp b/editor/src/inspector.cpp new file mode 100644 index 0000000..e714ec1 --- /dev/null +++ b/editor/src/inspector.cpp @@ -0,0 +1,187 @@ +#include "inspector.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inspectors/inspectorbase.h" +#include "inspectors/camerainspector.h" +#include "inspectors/lightinspector.h" +#include "inspectors/meshrendererinspector.h" +#include "inspectors/transforminspector.h" +#include "inspectors/materialinspector.h" +#include "inspectors/scriptactorinspector.h" +#include "inspectors/boxcolliderinspector.h" +#include "inspectors/rigidbodyinspector.h" +#include "inspectors/playerstartinspector.h" +#include "inspectors/modelinspector.h" +#include "inspectors/mapinspector.h" +#include "inspectors/cubemapinspector.h" +#include "inspectors/envprobeinspector.h" +#include "global.h" + +Inspector::Inspector(QWidget *parent) : QFrame(parent) +{ + setWindowTitle("Inspector"); + + m_masterlayout = new QVBoxLayout(this); + + m_layout2 = new QGridLayout(); + m_layout2->setAlignment(Qt::AlignTop); + m_layout2->setSpacing(0); + + m_scrollArea = new QScrollArea(); + m_scrollArea->setWidgetResizable(true); + + m_itemsContainer = new QFrame(m_scrollArea); + m_itemsContainer->setLayout(m_layout2); + + m_scrollArea->setWidget(m_itemsContainer); + + m_masterlayout->addWidget(m_scrollArea); + + m_label = new QLabel("Nothing selected."); + m_layout2->addWidget(m_label, 0, 0); + + QTimer* timer = new QTimer(this); + connect(timer, &QTimer::timeout, [=](){ Rebuild(); }); + timer->start(1); + + m_addComponentMenu = new QMenu("Add Component"); + + TypeTracker::RebuildTypes(); + + auto types = TypeTracker::GetTypeNames(); + for(unsigned int i = 0; i < types.size(); i++) + { + QAction* action = new QAction(QString(types[i].c_str())); + connect(action, &QAction::triggered, [=](bool) + { + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + if(entity != nullptr) + { + TypeTracker::RebuildTypes(); + entity->Add(types[i]); + } + }); + m_addComponentMenu->addAction(action); + } + + m_addComponentButton = new QPushButton("Add Component"); + m_addComponentButton->setDisabled(true); + m_masterlayout->addWidget(m_addComponentButton); + + m_addComponentButton->setMenu(m_addComponentMenu); + + //components + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + + //assets + Register(); + Register(); + Register(); + Register(); + Register(); + + GetEntPool().onEntityChange.Register([=]() { + Rebuild(true); + }); +} + +Inspector::~Inspector() +{ + +} + +QFrame* Inspector::GetInspector(std::type_index type, Component *component) +{ + for(auto& iter : m_inspectors) + if(iter.first == type) + return iter.second->Clone(component); + + return nullptr; +} + +QFrame* Inspector::GetInspectorAsset(Asset* asset) +{ + for(auto& iter : m_inspectors) + if(iter.first == asset->GetType()) + return iter.second->Clone(asset); + + return nullptr; +} + +void Inspector::Rebuild(bool override) +{ + if(GetState().selectedID == m_lastEntitySelected && !override) + return; + + while(!m_inspectorFrames.empty()) + { + delete m_inspectorFrames.back(); + m_inspectorFrames.pop_back(); + } + + if(GetState().isSelectionEntity) + { + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + + if(entity != nullptr) + { + m_label->setText(QString("Name: ") + QString(entity->name.c_str())); + + auto components = GetEntPool().GetAllComponents(); + + for(auto& component : components) + { + if(component.first.entity == entity) + { + QFrame* inspector = GetInspector(component.first.type, component.second); + if(inspector != nullptr) + { + m_layout2->addWidget(inspector); + + m_inspectorFrames.push_back(inspector); + } + } + } + + m_lastEntitySelected = GetState().selectedID; + + m_addComponentButton->setDisabled(false); + } + } + else + { + Asset* asset = AssetManager::GetAsset(GetState().selectedID); + + if(asset != nullptr) + { + m_label->setText(QString("Asset: ") + QString(asset->GetName().c_str())); + + QFrame* inspector = GetInspectorAsset(asset); + if(inspector != nullptr) + { + m_layout2->addWidget(inspector); + + m_inspectorFrames.push_back(inspector); + } + + m_lastEntitySelected = GetState().selectedID; + + m_addComponentButton->setDisabled(true); + } + } +} diff --git a/editor/src/inspectors/boxcolliderinspector.cpp b/editor/src/inspectors/boxcolliderinspector.cpp new file mode 100644 index 0000000..58898c9 --- /dev/null +++ b/editor/src/inspectors/boxcolliderinspector.cpp @@ -0,0 +1,12 @@ +#include "inspectors/boxcolliderinspector.h" + +BoxColliderInspector::BoxColliderInspector(BoxCollider* boxCollider, QWidget* parent) : InspectorBase(parent) +{ + if(!boxCollider) + return; + + Setup(boxCollider); + + AddProperty("Box size", boxCollider->m_size); + AddProperty("Box origin", boxCollider->m_origin); +} diff --git a/editor/src/inspectors/camerainspector.cpp b/editor/src/inspectors/camerainspector.cpp new file mode 100644 index 0000000..6d70de3 --- /dev/null +++ b/editor/src/inspectors/camerainspector.cpp @@ -0,0 +1,25 @@ +#include "inspectors/camerainspector.h" + +#include "global.h" + +CameraInspector::CameraInspector(Camera* camera, QWidget *parent) : InspectorBase(parent) +{ + if(!camera) + return; + + Setup(camera); + + AddProperty("Field of View", camera->fieldOfView); + + QPushButton* copyButton = new QPushButton("Copy from editor camera"); + connect(copyButton, &QPushButton::clicked, [=]() + { + IntermediateData transformData; + + GetState().editorCamera->GetTransform()->Save(transformData); + + camera->GetEntity()->GetTransform()->Load(transformData); + }); + + AddCustomProperty("", copyButton); +} diff --git a/editor/src/inspectors/cubemapinspector.cpp b/editor/src/inspectors/cubemapinspector.cpp new file mode 100644 index 0000000..360ec92 --- /dev/null +++ b/editor/src/inspectors/cubemapinspector.cpp @@ -0,0 +1,21 @@ +#include "inspectors/cubemapinspector.h" + +CubemapInspector::CubemapInspector(Cubemap* cubemap, QWidget* parent) : InspectorBase(parent) +{ + if(!cubemap) + return; + + Setup(nullptr, false); + + AddProperty("Right (+X)", Select_Texture, cubemap->faceIDs[0]); + AddProperty("Left (-X)", Select_Texture, cubemap->faceIDs[1]); + AddProperty("Top (+Y)", Select_Texture, cubemap->faceIDs[2]); + AddProperty("Bottom (-Y)", Select_Texture, cubemap->faceIDs[3]); + AddProperty("Front (+Z)", Select_Texture, cubemap->faceIDs[4]); + AddProperty("Back (-Z)", Select_Texture, cubemap->faceIDs[5]); + + connect(this, &InspectorBase::onChange, [=]() + { + cubemap->SaveCube(); + }); +} diff --git a/editor/src/inspectors/envprobeinspector.cpp b/editor/src/inspectors/envprobeinspector.cpp new file mode 100644 index 0000000..bbe0ef7 --- /dev/null +++ b/editor/src/inspectors/envprobeinspector.cpp @@ -0,0 +1,9 @@ +#include "inspectors/envprobeinspector.h" + +EnvironmentProbeInspector::EnvironmentProbeInspector(EnvironmentProbe* probe, QWidget* parent) : InspectorBase(parent) +{ + if(!probe) + return; + + Setup(probe); +} diff --git a/editor/src/inspectors/inspectorbase.cpp b/editor/src/inspectors/inspectorbase.cpp new file mode 100644 index 0000000..aa2d184 --- /dev/null +++ b/editor/src/inspectors/inspectorbase.cpp @@ -0,0 +1,160 @@ +#include "inspectors/inspectorbase.h" + +#include +#include + +#include "global.h" +#include "vector3widget.h" +#include "idselector.h" +#include "colorwidget.h" +#include "quaternionwidget.h" + +void InspectorBase::AddProperty(QString name, glm::vec3& ref) +{ + Vector3Widget* widget = new Vector3Widget(ref); + + QLabel* label = new QLabel(name); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); + + connect(widget, &Vector3Widget::onValueChanged, [=]() + { + emit onChange(); + }); +} + +void InspectorBase::AddProperty(QString name, glm::quat& ref) +{ + QuaternionWidget* widget = new QuaternionWidget(ref); + + QLabel* label = new QLabel(name); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); + + connect(widget, &QuaternionWidget::onValueChanged, [=]() + { + emit onChange(); + }); +} + +void InspectorBase::AddProperty(QString name, float& ref, float rate) +{ + QDoubleSpinBox* widget = new QDoubleSpinBox(); + widget->setValue(ref); + widget->setSingleStep(rate); + + connect(widget, static_cast(&QDoubleSpinBox::valueChanged), [&](double val) + { + ref = static_cast(val); + emit onChange(); + }); + + QLabel* label = new QLabel(name); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); +} + +void InspectorBase::AddProperty(QString name, ObjectSelectionType type, int& ref) +{ + IDSelector* widget = new IDSelector(type, ref); + + QLabel* label = new QLabel(name); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); + + connect(widget, &IDSelector::onValueChanged, [=]() + { + emit onChange(); + }); +} + +void InspectorBase::AddCustomProperty(QString name, QWidget* widget) +{ + QLabel* label = new QLabel(name); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); +} + +void InspectorBase::AddColorProperty(QString name, glm::vec3& ref) +{ + QLabel* label = new QLabel(name); + + ColorWidget* color = new ColorWidget(ref); + + int row = m_layout->rowCount(); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(color, row, 1); + + connect(color, &ColorWidget::onValueChanged, [=]() + { + emit onChange(); + }); +} + +void InspectorBase::AddProperty(QString name, bool& ref) +{ + QLabel* label = new QLabel(name); + + QCheckBox* widget = new QCheckBox(); + widget->setChecked(ref); + + int row = m_layout->rowCount(); + + connect(widget, &QCheckBox::clicked, [&](bool val) + { + ref = val; + emit onChange(); + }); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); +} + +void InspectorBase::AddProperty(QString name, std::string& ref) +{ + QLabel* label = new QLabel(name); + + QLineEdit* widget = new QLineEdit(); + widget->setText(ref.c_str()); + + int row = m_layout->rowCount(); + + connect(widget, &QLineEdit::textChanged, [&](const QString& text) + { + ref = text.toStdString(); + emit onChange(); + }); + + m_layout->addWidget(label, row, 0); + m_layout->addWidget(widget, row, 1); +} + +void InspectorBase::RemoveButtonClicked(bool) +{ + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + if(entity != nullptr) + entity->Remove(m_componentHandle); +} + +void InspectorBase::DuplicateButtonClicked(bool checked) +{ + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + if(entity != nullptr) + entity->Duplicate(m_componentHandle); +} \ No newline at end of file diff --git a/editor/src/inspectors/lightinspector.cpp b/editor/src/inspectors/lightinspector.cpp new file mode 100644 index 0000000..d2d5d78 --- /dev/null +++ b/editor/src/inspectors/lightinspector.cpp @@ -0,0 +1,41 @@ +#include "inspectors/lightinspector.h" + +#include +#include + +LightInspector::LightInspector(Light* light, QWidget* parent) : InspectorBase(parent) +{ + if(!light) + return; + + Setup(light); + + AddColorProperty("Color", light->color); + AddProperty("Radius", light->radius); + + QComboBox* typeComboBox = new QComboBox; + typeComboBox->insertItem(0, "Directional"); + typeComboBox->insertItem(1, "Point"); + + typeComboBox->setCurrentIndex(light->type); + + connect(typeComboBox, static_cast(&QComboBox::currentIndexChanged), [=](int index) + { + light->type = index; + }); + + AddCustomProperty("Type", typeComboBox); + AddProperty("Shadows enabled", light->shadowsEnabled); + + QSpinBox* shadowsizeBox = new QSpinBox(); + shadowsizeBox->setValue(light->shadowFrustumSize); + + connect(shadowsizeBox, static_cast(&QSpinBox::valueChanged), [=](int val) + { + light->shadowFrustumSize = val; + }); + + AddCustomProperty("Shadow Frustum Size", shadowsizeBox); + + AddProperty("Shadow Z-Far", light->shadowZFar); +} diff --git a/editor/src/inspectors/mapinspector.cpp b/editor/src/inspectors/mapinspector.cpp new file mode 100644 index 0000000..cb5dc72 --- /dev/null +++ b/editor/src/inspectors/mapinspector.cpp @@ -0,0 +1,22 @@ +#include "inspectors/mapinspector.h" + +#include +#include + +#include "global.h" + +MapInspector::MapInspector(Map* map, QWidget* parent) : InspectorBase(parent) +{ + if(!map) + return; + + Setup(nullptr, false); + + QPushButton* openButton = new QPushButton("Open map"); + connect(openButton, &QPushButton::clicked, [=]() + { + GetState().engine->GetGame()->LoadMap(map->GetPath(), true); + }); + + AddCustomProperty("", openButton); +} diff --git a/editor/src/inspectors/materialinspector.cpp b/editor/src/inspectors/materialinspector.cpp new file mode 100644 index 0000000..61710a8 --- /dev/null +++ b/editor/src/inspectors/materialinspector.cpp @@ -0,0 +1,28 @@ +#include "inspectors/materialinspector.h" + +MaterialInspector::MaterialInspector(Material* material, QWidget* parent) : InspectorBase(parent) +{ + if(!material) + return; + + Setup(nullptr, false); + + AddProperty("Albedo Texture", Select_Texture, material->albedoID); + AddColorProperty("Albedo Color", material->albedoColor); + + AddColorProperty("Emission Color", material->emissionColor); + + AddProperty("Normal Texture", Select_Texture, material->normalID); + + AddProperty("Metallic Texture", Select_Texture, material->metallicID); + AddProperty("Metallic", material->metallic, 0.1f); + + AddProperty("Roughness Texture", Select_Texture, material->roughnessID); + AddProperty("Roughness", material->roughness, 0.1f); + + connect(this, &InspectorBase::onChange, [=]() + { + material->invalidated = true; + material->SaveMat(); + }); +} diff --git a/editor/src/inspectors/meshrendererinspector.cpp b/editor/src/inspectors/meshrendererinspector.cpp new file mode 100644 index 0000000..f14c66b --- /dev/null +++ b/editor/src/inspectors/meshrendererinspector.cpp @@ -0,0 +1,22 @@ +#include "inspectors/meshrendererinspector.h" + +#include +#include +#include +#include + +MeshRendererInspector::MeshRendererInspector(MeshRenderer* mrenderer, QWidget* parent) : InspectorBase(parent) { + if(!mrenderer) + return; + + Setup(mrenderer); + + AddProperty("Mesh", Select_Mesh, mrenderer->meshID); + AddProperty("Material", Select_Material, mrenderer->materialID); + AddProperty("Static Geometry", mrenderer->isStaticGeometry); + + connect(this, &InspectorBase::onChange, [=]() + { + GetState().engine->GetRenderer()->GetDrawList().InvalidateNextFrame(); + }); +} \ No newline at end of file diff --git a/editor/src/inspectors/modelinspector.cpp b/editor/src/inspectors/modelinspector.cpp new file mode 100644 index 0000000..35c29c7 --- /dev/null +++ b/editor/src/inspectors/modelinspector.cpp @@ -0,0 +1,57 @@ +#include "inspectors/modelinspector.h" + +#include +#include + +#include "global.h" + +int mat; + +ModelInspector::ModelInspector(Model* model, QWidget* parent) : InspectorBase(parent) +{ + if(!model) + return; + + Setup(nullptr, false); + + AddProperty("Material", Select_Material, mat); + + QPushButton* placeButton = new QPushButton("Place in map"); + connect(placeButton, &QPushButton::clicked, [=]() + { + if(GetState().engine->GetGame()->GetWorld() != nullptr) + { + Entity* parentEntity = GetEntPool().CreateEntity(model->GetName(), GetState().engine->GetGame()->GetWorld()); + + auto children = AssetManager::GetChildren(model); + for(auto child : children) + { + //check if its a mesh + if(AssetManager::Get(child.first)) + { + Mesh* mesh = AssetManager::Get(child.first); + + Material* material = AssetManager::GetByName(model->submaterials[model->submeshes[mesh->GetIndex()].materialIndex].name); + + Entity* subMeshEntity = GetEntPool().CreateEntity(child.second->GetName(), + GetState().engine->GetGame()->GetWorld()); + subMeshEntity->Add()->meshID = child.first; + if(material) + { + subMeshEntity->Get()->materialID = AssetManager::GetID(material); + } + else + { + subMeshEntity->Get()->materialID = mat; + } + //TODO: will make a seperate checkbox + //subMeshEntity->Get()->isStaticGeometry = true; + + subMeshEntity->GetTransform()->SetParent(parentEntity->GetTransform()); + } + } + } + }); + + AddCustomProperty("", placeButton); +} diff --git a/editor/src/inspectors/playerstartinspector.cpp b/editor/src/inspectors/playerstartinspector.cpp new file mode 100644 index 0000000..def7d5c --- /dev/null +++ b/editor/src/inspectors/playerstartinspector.cpp @@ -0,0 +1,9 @@ +#include "inspectors/playerstartinspector.h" + +PlayerStartInspector::PlayerStartInspector(PlayerStart* pstart, QWidget* parent) : InspectorBase(parent) +{ + if(!pstart) + return; + + Setup(pstart); +} \ No newline at end of file diff --git a/editor/src/inspectors/rigidbodyinspector.cpp b/editor/src/inspectors/rigidbodyinspector.cpp new file mode 100644 index 0000000..ad29694 --- /dev/null +++ b/editor/src/inspectors/rigidbodyinspector.cpp @@ -0,0 +1,16 @@ +#include "inspectors/rigidbodyinspector.h" + +RigidbodyInspector::RigidbodyInspector(Rigidbody* rigidbody, QWidget* parent) : InspectorBase(parent) +{ + if(!rigidbody) + return; + + Setup(rigidbody); + + AddProperty("Kinematic", rigidbody->m_kinematic); + AddProperty("Enable deactivation", rigidbody->m_enableDeactivation); + AddProperty("Enable rotation", rigidbody->m_enableRotation); + + AddProperty("Mass", rigidbody->m_mass); + AddProperty("Friction", rigidbody->m_friction); +} \ No newline at end of file diff --git a/editor/src/inspectors/scriptactorinspector.cpp b/editor/src/inspectors/scriptactorinspector.cpp new file mode 100644 index 0000000..779c841 --- /dev/null +++ b/editor/src/inspectors/scriptactorinspector.cpp @@ -0,0 +1,11 @@ +#include "inspectors/scriptactorinspector.h" + +ScriptActorInspector::ScriptActorInspector(ScriptActor* scriptActor, QWidget* parent) : InspectorBase(parent) +{ + if(!scriptActor) + return; + + Setup(scriptActor); + + AddProperty("Script path", scriptActor->scriptPath); +} diff --git a/editor/src/inspectors/transforminspector.cpp b/editor/src/inspectors/transforminspector.cpp new file mode 100644 index 0000000..f9167a1 --- /dev/null +++ b/editor/src/inspectors/transforminspector.cpp @@ -0,0 +1,33 @@ +#include "inspectors/transforminspector.h" + +#include +#include +#include + +TransformInspector::TransformInspector(Transform* transform, QWidget* parent) : InspectorBase(parent) +{ + if(!transform) + return; + + Setup(transform, false); + + AddProperty("Position", transform->position); + AddProperty("Orientation", transform->orientation); + AddProperty("Scale", transform->scale); + + AddProperty("Parent", Select_Entity, transform->parentID); + + connect(this, &InspectorBase::onChange, [=]() + { + //disabled for the time being because it keeps triggering this even if nothing is moved + //GetState().renderer->GetDisplayList().InvalidateNextFrame(); + }); + + QPushButton* rebuildButton = new QPushButton("Rebuild static"); + connect(rebuildButton, &QPushButton::clicked, [=]() + { + GetState().engine->GetRenderer()->GetDrawList().InvalidateNextFrame(); + }); + + AddCustomProperty("", rebuildButton); +} diff --git a/editor/src/main.cpp b/editor/src/main.cpp new file mode 100644 index 0000000..339f6b3 --- /dev/null +++ b/editor/src/main.cpp @@ -0,0 +1,16 @@ +#include + +#include "mainwindow.h" +#include "editorstyle.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + + a.setStyle(new EditorStyle); + + MainWindow mainWindow; + mainWindow.show(); + + return a.exec(); +} diff --git a/editor/src/mainwindow.cpp b/editor/src/mainwindow.cpp new file mode 100644 index 0000000..8222970 --- /dev/null +++ b/editor/src/mainwindow.cpp @@ -0,0 +1,441 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exportwindow.h" +#include "projectwindow.h" +#include "assetbrowser.h" +#include "gamesettings.h" +#include "global.h" +#include "worldsettings.h" + +void error_callback(int, const char* description) +{ + Log::Error("GLFW Error: %s", description); +} + +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::MainWindow) +{ + m_ui->setupUi(this); + + std::ifstream in("editor.json"); + if(in.is_open()) + { + try + { + nlohmann::json file; + file << in; + + GetState().projectRoot = file["currentproject"]; + } + catch(std::exception& exception) + { + Log::Error("Failed to parse editor.json! (%s)", exception.what()); + } + } + else + { + ProjectWindow* projectWindow = new ProjectWindow(this); + projectWindow->show(); + } + + m_manager = new ToolWindowManager; + setCentralWidget(m_manager); + + m_manager->setRubberBandLineWidth(50); + + QAction* playAction = m_ui->toolBar->addAction("Play"); + connect(playAction, &QAction::triggered, [=]() + { + if(!GetState().engine->GetGame()->GetWorld()) + return; + + /* + * this should be replaced later by a system that allows us to duplicate all the entities into a new world, + * but for now this works. + */ + if(!GetState().isCurrentlyPlaying) + { + GetState().temporaryEntityState = GetEntPool(); + + GetEntPool().Delete(GetState().editorCamera); + GetEntPool().ProcessDeletions(); + + GetState().engine->GetGame()->CreateActors(); + + GetState().isCurrentlyPlaying = true; + } + else + { + StopPlaying(); + } + + RefreshWindowTitle(); + }); + + glfwSetErrorCallback(error_callback); + if(!glfwInit()) + { + Log::Error("Failed to initialize GLFW!"); + return; + } + + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + GLFWwindow* window = glfwCreateWindow(constants::defaultScreenWidth, constants::defaultScreenHeight, "Viewport", nullptr, nullptr); + if(window == nullptr) + { + Log::Error("Failed to create GLFW window!"); + return; + } + + Filesystem::Init(nullptr, {}); + Filesystem::IsPacked() = false; //project files aren't packed + + Platform::SetUserData(static_cast(window)); + InputPump::Initialize(); + + GFX::GetScreenWidth() = constants::defaultScreenWidth; + GFX::GetScreenHeight() = constants::defaultScreenHeight; + + GameInfo info; + info.gameName = "editor"; + info.defaultMap = ""; + info.debugGUIEnabled = false; + info.isEditor = true; + + GetState().window = window; + GetState().engine = new Engine(info); + + RefreshWindowTitle(); + + QTimer* timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [=]() + { + if(!GetState().isCurrentlyPlaying) + { + if(Input::IsMouseClicked(MouseButton::Left)) + { + double x, y; + Input::GetMousePosition(x, y); + + //opengl::SelectionResult selection = static_cast(GetState().engine->GetRenderer())->GetDebugPass()->DoSelection(x, y); + //GetState().selectedID = selection.id; + //GetState().isSelectionEntity = true; + } + + if(!Input::IsMouseDown(MouseButton::Right)) + { + m_first = true; + } + + if(Input::IsMouseDown(MouseButton::Right)) + { + double x, y; + Input::GetMousePosition(x, y); + + if(m_first) + { + m_lastX = x; + m_lastY = y; + m_first = false; + } + + float xoffset = static_cast(m_lastX - x); + float yoffset = static_cast(m_lastY - y); + + xoffset *= 0.01f; + yoffset *= 0.01f; + + m_lastX = x; + m_lastY = y; + + m_yaw += xoffset; + m_pitch += yoffset; + + GetState().editorCamera->Get()->fieldOfView -= Input::GetScrollDelta(); + GetState().editorCamera->GetTransform()->orientation = + glm::angleAxis(m_yaw, glm::vec3(0, 1, 0)) * glm::angleAxis(m_pitch, glm::vec3(1, 0, 0)); + + glm::vec3 forward = glm::normalize(GetState().editorCamera->GetTransform()->GetForward()); + glm::vec3 right = glm::normalize(GetState().editorCamera->GetTransform()->GetRight()); + + float multiplier = 0.01f; + if(Input::IsKeyDown(Key::LeftShift)) + multiplier = 0.05f; + + if(Input::IsKeyDown(Key::W)) + GetState().editorCamera->GetTransform()->position -= multiplier * forward * 16.67f; + + if(Input::IsKeyDown(Key::A)) + GetState().editorCamera->GetTransform()->position -= multiplier * right * 16.67f; + + if(Input::IsKeyDown(Key::S)) + GetState().editorCamera->GetTransform()->position += multiplier * forward * 16.67f; + + if(Input::IsKeyDown(Key::D)) + GetState().editorCamera->GetTransform()->position += multiplier * right * 16.67f; + } + + //GetState().engine->GetRenderer()->SetSelection(GetState().selectedID); + } + else + { + if(Input::IsKeyDown(Key::J)) + StopPlaying(); + + //set mouse to center of viewport + cursor().setPos(m_viewport->mapToGlobal(QPoint(m_viewport->width() / 2, m_viewport->height() / 2))); + + //GetState().engine->GetRenderer()->SetSelection(-1); + } + + GetState().engine->Tick(GetState().isCurrentlyPlaying); + }); + timer->start(1); + + CreateCamera(); + + m_viewport = new Viewport; + m_manager->addToolWindow(m_viewport, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); + m_manager->setToolWindowProperties(m_viewport, ToolWindowManager::HideOnClose); + + m_manager->addToolWindow(new Hierarchy, ToolWindowManager::AreaReference(ToolWindowManager::LeftOf, m_manager->areaOf(m_viewport), 0.15f)); + m_manager->addToolWindow(new Inspector, ToolWindowManager::AreaReference(ToolWindowManager::RightOf, m_manager->areaOf(m_viewport), 5.55f)); + m_manager->addToolWindow(new AssetBrowser, ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, m_manager->areaOf(m_viewport), 1.0f)); + + AssetManager::SearchDisk(GetState().projectRoot); + + m_ui->actionNew->setShortcut(tr("CTRL+N")); + m_ui->actionOpen->setShortcut(tr("CTRL+O")); + m_ui->actionSave->setShortcut(tr("CTRL+S")); + m_ui->actionExit->setShortcut(tr("ALT+F4")); + + m_ui->actionDuplicate->setShortcut(tr("CTRL+D")); + m_ui->actionDelete->setShortcut(tr("DEL")); +} + +MainWindow::~MainWindow() +{ + delete GetState().engine; + + glfwDestroyWindow(GetState().window); + + delete m_ui; +} + +void MainWindow::on_actionOpen_triggered() +{ + QString filepath = QFileDialog::getOpenFileName(this, "Open...", GetState().projectRoot.c_str(), tr("Map files (*.vmap)")); + if(filepath != "") + { + GetState().engine->GetGame()->LoadMap(filepath.toStdString(), true); + + RefreshWindowTitle(); + } +} + +void MainWindow::on_actionHierarchy_triggered() +{ + Hierarchy* hierarchy = new Hierarchy; + hierarchy->setWindowTitle("Hierarchy"); + + m_manager->addToolWindow(hierarchy, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); +} + +void MainWindow::on_actionInspector_triggered() +{ + Inspector* inspector = new Inspector; + inspector->setWindowTitle("Inspector"); + + m_manager->addToolWindow(inspector, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); +} + +void MainWindow::on_actionAssetBrowser_triggered() +{ + AssetBrowser* assetBrowser = new AssetBrowser; + assetBrowser->setWindowTitle("Asset Browser"); + + m_manager->addToolWindow(assetBrowser, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); +} + +void MainWindow::on_actionViewport_triggered() +{ + //protects against multiple viewports + if(m_manager->toolWindows().contains(m_viewport)) + m_manager->addToolWindow(m_viewport, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); + + if(m_viewport == nullptr) + { + Viewport* viewport = new Viewport; + viewport->setWindowTitle("Viewport"); + + m_manager->addToolWindow(viewport, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); + } +} + +void MainWindow::on_actionExit_triggered() +{ + if(SaveConfirmation()) + QCoreApplication::quit(); +} + +void MainWindow::on_actionNew_triggered() +{ + GetState().engine->GetGame()->CreateEmptyWorld(); + + RefreshWindowTitle(); +} + +void MainWindow::on_actionClose_triggered() +{ + if(GetState().engine->GetGame()->GetWorld() != nullptr) + { + if(QMessageBox::critical(this, + "Close Confirmation", + "Are you sure you want to close the current map?", + QMessageBox::Yes, + QMessageBox::Cancel) == QMessageBox::Yes) + { + GetState().engine->GetGame()->CleanupWorld(); + + RefreshWindowTitle(); + } + } +} + +void MainWindow::on_actionSave_triggered() +{ + if(GetState().engine->GetGame()->GetWorld() != nullptr) + { + QString filepath = QFileDialog::getSaveFileName(this, "Save", GetState().projectRoot.c_str(), tr("Map files (*.vmap)")); + if(filepath != "") + AssetManager::CreateMap(GetState().engine->GetGame()->GetWorld(), filepath.toStdString()); + } +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + if(SaveConfirmation()) + { + event->accept(); + } + else + { + event->ignore(); + } +} + +bool MainWindow::SaveConfirmation() +{ + if(GetState().engine->GetGame()->GetWorld() == nullptr) + return true; + + if(QMessageBox::critical(this, + "Exit Confirmation", + "Do you really want to exit?", + QMessageBox::Yes, + QMessageBox::Cancel) == QMessageBox::Cancel) + { + return false; + } + + return true; +} + +void MainWindow::on_actionExport_triggered() +{ + ExportWindow* window = new ExportWindow(); + window->show(); +} + +void MainWindow::on_actionEditGameSettings_triggered() +{ + GameSettings* gameSettings = new GameSettings(); + gameSettings->show(); +} + +void MainWindow::on_actionEditWorldSettings_triggered() +{ + if(GetState().engine->GetGame()->GetWorld()) + { + WorldSettings* worldSettings = new WorldSettings(); + worldSettings->show(); + } +} + +void MainWindow::on_actionDuplicate_triggered() +{ + if(GetState().engine->GetGame()->GetWorld() == nullptr) + return; + + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + if(entity != nullptr) + GetEntPool().Duplicate(entity, GetState().engine->GetGame()->GetWorld()); +} + +void MainWindow::on_actionDelete_triggered() +{ + Entity* entity = GetEntPool().GetByID(GetState().selectedID); + if(entity != nullptr) + GetEntPool().Delete(entity); +} + +void MainWindow::StopPlaying() +{ + GetState().isCurrentlyPlaying = false; + + GetEntPool() = GetState().temporaryEntityState; + + //force refresh entity lists + GetEntPool().onEntityListChange(); + + RefreshWindowTitle(); +} + +void MainWindow::RefreshWindowTitle() +{ + if(GetState().engine->GetGame()->GetWorld() != nullptr) + { + std::string path = GetState().engine->GetGame()->GetWorld()->GetPath(); + setWindowTitle((std::string(constants::engineName) + " Editor - " + (path.length() ? path : "New world") + (GetState().isCurrentlyPlaying ? " - Press J to stop playing" : "")).c_str()); + } + else + { + setWindowTitle((std::string(constants::engineName) + " Editor").c_str()); + } +} + +void MainWindow::on_actionSwitch_triggered() +{ + on_actionClose_triggered(); + + ProjectWindow* projectWindow = new ProjectWindow(this); + projectWindow->show(); +} + +void MainWindow::CreateCamera() +{ + GetState().editorCamera = GetEntPool().CreateEntity("Editor Camera", nullptr); + GetState().editorCamera->tag = "editor"; + GetState().editorCamera->Add()->fieldOfView = 60.0f; +} \ No newline at end of file diff --git a/editor/src/objectselectiondialog.cpp b/editor/src/objectselectiondialog.cpp new file mode 100644 index 0000000..7e33573 --- /dev/null +++ b/editor/src/objectselectiondialog.cpp @@ -0,0 +1,101 @@ +#include "objectselectiondialog.h" + +#include +#include +#include + +ObjectSelectionDialog::ObjectSelectionDialog(ObjectSelectionType type, int& ref) : m_reference(ref) +{ + setWindowTitle("Select Asset"); + setMinimumWidth(250); + + m_layout = new QVBoxLayout(this); + + m_scrollArea = new QScrollArea(); + m_scrollArea->setWidgetResizable(true); + + m_itemsLayout = new QVBoxLayout(); + + m_itemsContainer = new QFrame(m_scrollArea); + m_itemsContainer->setLayout(m_itemsLayout); + + m_scrollArea->setWidget(m_itemsContainer); + + m_layout->addWidget(m_scrollArea); + + AddToList("None", 0); + + //TODO: remove duplicate loop code (make a general GetAssets() function?) + switch(type) + { + case Select_Mesh: + { + setWindowTitle("Select Mesh..."); + + auto meshes = AssetManager::GetMeshes(); + for(auto& itr : meshes) + AddToList(itr.second->GetName(), itr.first); + } + break; + case Select_Texture: + { + setWindowTitle("Select Texture..."); + + auto textures = AssetManager::GetTextures(); + for(auto& itr : textures) + AddToList(itr.second->GetName(), itr.first); + } + break; + case Select_Material: + { + setWindowTitle("Select Material..."); + + auto materials = AssetManager::GetMaterials(); + for(auto& itr : materials) + AddToList(itr.second->GetName(), itr.first); + } + break; + case Select_Map: + { + setWindowTitle("Select Map..."); + + auto maps = AssetManager::GetMaps(); + for(auto& itr : maps) + AddToList(itr.second->GetName(), itr.first); + } + break; + case Select_Entity: + { + setWindowTitle("Select Entity..."); + + auto entities = GetEntPool().GetAllEntities(); + for(auto& entity : entities) + AddToList(entity->name, entity->GetID()); + } + break; + case Select_Cubemap: + { + setWindowTitle("Select Cubemap..."); + + auto maps = AssetManager::GetCubemaps(); + for(auto& itr : maps) + AddToList(itr.second->GetName(), itr.first); + } + break; + } +} + +void ObjectSelectionDialog::AddToList(const std::string& name, int id) +{ + QPushButton* pushButton = new QPushButton(name.c_str()); + + if(m_reference == id) + pushButton->setDisabled(true); + + connect(pushButton, &QPushButton::pressed, [=]() + { + m_reference = id; + close(); + }); + m_itemsLayout->addWidget(pushButton); +} \ No newline at end of file diff --git a/editor/src/projectwindow.cpp b/editor/src/projectwindow.cpp new file mode 100644 index 0000000..12619c4 --- /dev/null +++ b/editor/src/projectwindow.cpp @@ -0,0 +1,91 @@ +#include "projectwindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" + +void ChangeProjectRoot() +{ + Log::Print("Set project root to %s!", GetState().projectRoot.c_str()); + + nlohmann::json file; + + file["currentproject"] = GetState().projectRoot; + + AssetManager::SearchDisk(GetState().projectRoot); //TODO: refresh asset browser on project root change + + std::ofstream out; + out.open("editor.json"); + + out << file.dump(1); + out.close(); +} + +ProjectWindow::ProjectWindow(QWidget* parent) : QWidget(parent) +{ + setWindowFlags(Qt::Dialog); + setWindowModality(Qt::ApplicationModal); + setFixedSize(800, 450); + + QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout* grouplayout = new QVBoxLayout(); + + QGroupBox* groupBox = new QGroupBox("New Project"); + groupBox->setLayout(grouplayout); + layout->addWidget(groupBox); + + QLineEdit* nameText = new QLineEdit(); + grouplayout->addWidget(nameText); + + QLineEdit* directoryText = new QLineEdit(); + grouplayout->addWidget(directoryText); + + directoryText->setText((getenv("HOME")+ std::string("/")).c_str()); + + QPushButton* createButton = new QPushButton("Create"); + grouplayout->addWidget(createButton); + + connect(createButton, &QPushButton::clicked, [=]() + { + Filesystem::CreateDirectory(directoryText->text().toStdString() + nameText->text().toStdString()); + GetState().projectRoot = directoryText->text().toStdString() + nameText->text().toStdString(); + + ChangeProjectRoot(); + + hide(); + }); + + QVBoxLayout* existingGroupLayout = new QVBoxLayout(); + + QGroupBox* existingGroupBox = new QGroupBox("Open Existing Project"); + existingGroupBox->setLayout(existingGroupLayout); + layout->addWidget(existingGroupBox); + + QPushButton* openButton = new QPushButton("Open..."); + existingGroupLayout->addWidget(openButton); + + connect(openButton, &QPushButton::clicked, [=]() + { + QString filepath = QFileDialog::getExistingDirectory(this, "Open...", getenv("HOME")); + + if(filepath != "") + { + GetState().projectRoot = filepath.toStdString(); + + ChangeProjectRoot(); + + hide(); + } + }); +} + +ProjectWindow::~ProjectWindow() +{ + +} \ No newline at end of file diff --git a/editor/src/quaternionwidget.cpp b/editor/src/quaternionwidget.cpp new file mode 100644 index 0000000..b6fa2e5 --- /dev/null +++ b/editor/src/quaternionwidget.cpp @@ -0,0 +1,65 @@ +#include "quaternionwidget.h" + +#include +#include + +QuaternionWidget::QuaternionWidget(glm::quat& ref, QWidget *parent) : QWidget(parent), m_reference(ref) +{ + QHBoxLayout* itemsLayout = new QHBoxLayout(this); + + m_spinBoxes.x = new QDoubleSpinBox(); + m_spinBoxes.y = new QDoubleSpinBox(); + m_spinBoxes.z = new QDoubleSpinBox(); + + m_spinBoxes.x->setMinimum(-360.0); + m_spinBoxes.x->setMaximum(360.0); + + m_spinBoxes.y->setMinimum(-360.0); + m_spinBoxes.y->setMaximum(360.0); + + m_spinBoxes.z->setMinimum(-360.0); + m_spinBoxes.z->setMaximum(360.0); + + itemsLayout->addWidget(m_spinBoxes.x); + itemsLayout->addWidget(m_spinBoxes.y); + itemsLayout->addWidget(m_spinBoxes.z); + + glm::vec3 euler = glm::eulerAngles(m_reference); + m_eulerRotation = glm::vec3(glm::degrees(euler.x), glm::degrees(euler.y), glm::degrees(euler.z)); + + m_spinBoxes.x->setValue(m_eulerRotation.x); + m_spinBoxes.y->setValue(m_eulerRotation.y); + m_spinBoxes.z->setValue(m_eulerRotation.z); + + connect(m_spinBoxes.x, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_eulerRotation.x = d; RebuildQuat(); }); + connect(m_spinBoxes.y, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_eulerRotation.y = d; RebuildQuat(); }); + connect(m_spinBoxes.z, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_eulerRotation.z = d; RebuildQuat(); }); + + //TODO: find a better way to do this + m_updateTimer = new QTimer; + connect(m_updateTimer, &QTimer::timeout, [=]() + { + glm::vec3 euler = glm::eulerAngles(m_reference); + m_eulerRotation = glm::vec3(glm::degrees(euler.x), glm::degrees(euler.y), glm::degrees(euler.z)); + + if(m_eulerRotation.x != m_spinBoxes.x->value() || m_eulerRotation.y != m_spinBoxes.y->value() || m_eulerRotation.z != m_spinBoxes.z->value()) + { + m_spinBoxes.x->setValue(m_eulerRotation.x); + m_spinBoxes.y->setValue(m_eulerRotation.y); + m_spinBoxes.z->setValue(m_eulerRotation.z); + + emit onValueChanged(); + } + }); + m_updateTimer->start(1); +} + +void QuaternionWidget::RebuildQuat() +{ + m_reference = glm::quat(glm::vec3(glm::radians(m_eulerRotation.x), glm::radians(m_eulerRotation.y), glm::radians(m_eulerRotation.z))); +} + +QuaternionWidget::~QuaternionWidget() +{ + m_updateTimer->stop(); +} \ No newline at end of file diff --git a/editor/src/vector3widget.cpp b/editor/src/vector3widget.cpp new file mode 100644 index 0000000..cc112a9 --- /dev/null +++ b/editor/src/vector3widget.cpp @@ -0,0 +1,53 @@ +#include "vector3widget.h" + +#include +#include + +Vector3Widget::Vector3Widget(glm::vec3& ref, QWidget *parent) : QWidget(parent), m_reference(ref) +{ + QHBoxLayout *itemsLayout = new QHBoxLayout(this); + + m_spinBoxes.x = new QDoubleSpinBox(); + m_spinBoxes.y = new QDoubleSpinBox(); + m_spinBoxes.z = new QDoubleSpinBox(); + + m_spinBoxes.x->setMinimum(-10000.0); + m_spinBoxes.x->setMaximum(10000.0); + + m_spinBoxes.y->setMinimum(-10000.0); + m_spinBoxes.y->setMaximum(10000.0); + + m_spinBoxes.z->setMinimum(-10000.0); + m_spinBoxes.z->setMaximum(10000.0); + + itemsLayout->addWidget(m_spinBoxes.x); + itemsLayout->addWidget(m_spinBoxes.y); + itemsLayout->addWidget(m_spinBoxes.z); + + m_spinBoxes.x->setValue(m_reference.x); + m_spinBoxes.y->setValue(m_reference.y); + m_spinBoxes.z->setValue(m_reference.z); + + connect(m_spinBoxes.x, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_reference.x = d; }); + connect(m_spinBoxes.y, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_reference.y = d; }); + connect(m_spinBoxes.z, static_cast(&QDoubleSpinBox::valueChanged), [=](double d){ m_reference.z = d; }); + + //TODO: find a better way to do this + m_updateTimer = new QTimer; + connect(m_updateTimer, &QTimer::timeout, [=]() + { + if(m_reference.x != m_spinBoxes.x->value() || m_reference.y != m_spinBoxes.y->value() || m_reference.z != m_spinBoxes.z->value()) { + m_spinBoxes.x->setValue(m_reference.x); + m_spinBoxes.y->setValue(m_reference.y); + m_spinBoxes.z->setValue(m_reference.z); + + emit onValueChanged(); + } + }); + m_updateTimer->start(1); +} + +Vector3Widget::~Vector3Widget() +{ + m_updateTimer->stop(); +} \ No newline at end of file diff --git a/editor/src/viewport.cpp b/editor/src/viewport.cpp new file mode 100644 index 0000000..ba790a3 --- /dev/null +++ b/editor/src/viewport.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "viewport.h" +#include "global.h" + +Viewport::Viewport(QWidget *parent) : QFrame(parent) +{ + setWindowTitle("Viewport"); + + m_layout = new QVBoxLayout(this); + + m_viewportFrame = new QFrame; + m_layout->addWidget(m_viewportFrame); + + m_toolbar = new QToolBar; + m_layout->addWidget(m_toolbar); + + m_toolbar->addWidget(new QLabel("test")); + +#ifdef LINUX + XReparentWindow(glfwGetX11Display(), glfwGetX11Window(GetState().window), m_viewportFrame->winId(), 0, 0); + XMapWindow(glfwGetX11Display(), glfwGetX11Window(GetState().window)); +#endif +} + +void Viewport::resizeEvent(QResizeEvent *event) +{ + if(event->size().width() != 0 && event->size().height() != 0) + { + GFX::GetScreenWidth() = event->size().width(); + GFX::GetScreenHeight() = event->size().height(); + + glfwSetWindowSize(GetState().window, event->size().width(), event->size().height()); + + GetState().engine->GetRenderer()->Resized(); + } +} diff --git a/editor/src/worldsettings.cpp b/editor/src/worldsettings.cpp new file mode 100644 index 0000000..579df50 --- /dev/null +++ b/editor/src/worldsettings.cpp @@ -0,0 +1,20 @@ +#include "worldsettings.h" + +#include "idselector.h" +#include "global.h" + +#include +#include +#include +#include + +WorldSettings::WorldSettings() +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + QLabel* gameNameLabel = new QLabel("Skybox"); + layout->addWidget(gameNameLabel); + + IDSelector* skyboxSelector = new IDSelector(Select_Cubemap, GetState().engine->GetGame()->GetWorld()->skyboxID); + layout->addWidget(skyboxSelector); +} \ No newline at end of file diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..c9281e5 --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(renderer) + +add_subdirectory(ecs) +add_subdirectory(platform) +add_subdirectory(assets) +add_subdirectory(input) +add_subdirectory(core) + +add_subdirectory(runtime) + diff --git a/engine/assets/CMakeLists.txt b/engine/assets/CMakeLists.txt new file mode 100644 index 0000000..5462f7e --- /dev/null +++ b/engine/assets/CMakeLists.txt @@ -0,0 +1,19 @@ +set(SOURCE_FILES + src/assetmanager.cpp + src/texture.cpp + src/mesh.cpp + src/skeleton.cpp + src/map.cpp + src/cubemap.cpp) + +add_library(Assets ${SOURCE_FILES}) + +target_link_libraries(Assets -Wl,-rpath,. Platform) + +include_directories(include) +include_directories(${UTILITY_INCLUDE_DIR} ${RENDERER_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} ${CORE_INCLUDE_DIR}) +include_directories(SYSTEM + ${LIBRARY_JSON_INCLUDE_DIR} + ${LIBRARY_STB_INCLUDE_DIR} + ${LIBRARY_GLM_INCLUDE_DIR} + ${LIBRARY_PHYSFS_INCLUDE_DIR}) diff --git a/engine/assets/include/animation.hpp b/engine/assets/include/animation.hpp new file mode 100644 index 0000000..48ab3b0 --- /dev/null +++ b/engine/assets/include/animation.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +class Animation : public Skeleton +{ +public: + +}; \ No newline at end of file diff --git a/engine/assets/include/asset.hpp b/engine/assets/include/asset.hpp new file mode 100644 index 0000000..8d84864 --- /dev/null +++ b/engine/assets/include/asset.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +class Renderer; + +class Asset +{ +public: + virtual ~Asset() {} + + virtual void Load(Renderer*) {} + virtual void Unload(Renderer*) {} + + virtual std::vector GetDependencies() + { + return std::vector(); + } + + void SetParent(Asset* asset) + { + m_parentAsset = asset; + } + + Asset* GetParent() const + { + return m_parentAsset; + } + + virtual std::type_index GetType() + { + return typeid(Asset); + } + + bool IsLoaded() + { + return m_loaded; + } + + virtual std::string GetName() + { + return std::string(); + } + + virtual std::string GetPath() + { + return std::string(); + } + +protected: + Asset* m_parentAsset = nullptr; + + bool m_loaded = false; +}; \ No newline at end of file diff --git a/engine/assets/include/assetmanager.hpp b/engine/assets/include/assetmanager.hpp new file mode 100644 index 0000000..22a3323 --- /dev/null +++ b/engine/assets/include/assetmanager.hpp @@ -0,0 +1,269 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asset.hpp" +#include "texture.hpp" +#include "material.hpp" +#include "model.hpp" +#include "mesh.hpp" +#include "skeleton.hpp" +#include "map.hpp" +#include "cubemap.hpp" + +class World; + +class AssetManager +{ +public: + static void SetRenderer(Renderer* renderer); + + static void Cleanup(); + + static void SearchDisk(const std::string& path); + static void SearchPacked(); + + static void LoadAssets(const std::vector& ids); + + static nlohmann::json ConstructMeta(Asset* asset, const int id); + + static void SaveMeta(const int id); + static void LoadMeta(const std::string& path); + static void ParseMeta(nlohmann::json file, std::string path, Asset* parentAsset = nullptr); + + static void ImportAsset(const std::string& filepath, const std::string& dest); + static void ImportModel(const std::string& filepath); + static Texture* ImportTexture(const std::string& filepath); + + static void DeleteAsset(Asset* asset); + + static Material* CreateMaterial(const std::string& filepath); + static Cubemap* CreateCubemap(const std::string& filepath); + static Map* CreateMap(World* world, const std::string& filepath); + + static std::vector GetDependencies(Asset* asset); + static std::map GetChildren(Asset* asset); + + static int GetID(const std::string& filepath) + { + return m_filepaths[filepath]; + } + + static int GetID(const Asset* asset) + { + for(auto iter : m_modelAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_meshAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_materialAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_textureAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_skeletonAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_mapAssets) + { + if(iter.second == asset) + return iter.first; + } + + for(auto iter : m_cubemapAssets) + { + if(iter.second == asset) + return iter.first; + } + + return 0; //if no asset is found + } + + static Asset* GetAsset(const int id) + { + try + { + if(m_modelAssets.count(id)) + return m_modelAssets.at(id); + + if(m_meshAssets.count(id)) + return m_meshAssets.at(id); + + if(m_materialAssets.count(id)) + return m_materialAssets.at(id); + + if(m_textureAssets.count(id)) + return m_textureAssets.at(id); + + if(m_skeletonAssets.count(id)) + return m_skeletonAssets.at(id); + + if(m_mapAssets.count(id)) + return m_mapAssets.at(id); + + if(m_cubemapAssets.count(id)) + return m_cubemapAssets.at(id); + } + catch(std::out_of_range exception) + { + Log::Error("Could not find asset %s!", id); + } + + return nullptr; + } + + template + static T* Get(const int) + { + static_assert(std::is_base_of::value, "Get<> requires to be given an Asset type!"); + + return nullptr; + } + + template + static T* GetByPath(const std::string& filepath) + { + static_assert(std::is_base_of::value, "Get<> requires to be given an Asset type!"); + + return Get(GetID(filepath)); + } + + template + static T* GetByName(const std::string& filepath) + { + static_assert(std::is_base_of::value, "Get<> requires to be given an Asset type!"); + + auto assets = GetAssets(); + for(auto asset : assets) + { + if(asset.second->GetPath().find(filepath) != std::string::npos) + return static_cast(asset.second); + } + + return nullptr; + } + + static std::vector GetDirectoryAssets(const std::string& directory); + + static std::map GetAssets() + { + std::map tmp; + + for(auto& itr : m_modelAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_meshAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_materialAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_textureAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_skeletonAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_mapAssets) + tmp.emplace(itr.first, itr.second); + + for(auto& itr : m_cubemapAssets) + tmp.emplace(itr.first, itr.second); + + return tmp; + } + + static std::map GetMeshes() + { + return m_meshAssets; + } + + static std::map GetTextures() + { + return m_textureAssets; + } + + static std::map GetMaterials() + { + return m_materialAssets; + } + + static std::map GetSkeletons() + { + return m_skeletonAssets; + } + + static std::map GetMaps() + { + return m_mapAssets; + } + + static std::map GetCubemaps() + { + return m_cubemapAssets; + } + + static std::vector GetFilepaths() + { + std::vector paths; + for(auto itr : m_filepaths) + { + std::string p = itr.first; + + paths.push_back(p); + } + + return paths; + } + + static EventDispatcher onAssetListUpdate; + +private: + static std::map m_filepaths; + static std::map m_modelAssets; + static std::map m_meshAssets; + static std::map m_materialAssets; + static std::map m_textureAssets; + static std::map m_skeletonAssets; + static std::map m_mapAssets; + static std::map m_cubemapAssets; + + static Renderer* m_renderer; +}; + +#define GET_ASSET(X, y) template<> inline X* AssetManager::Get(const int id) { try { return y.at(id); } catch(std::out_of_range exception) { return nullptr; } } + +//Get template definitions +GET_ASSET(Model, m_modelAssets) +GET_ASSET(Mesh, m_meshAssets) +GET_ASSET(Material, m_materialAssets) +GET_ASSET(Texture, m_textureAssets) +GET_ASSET(Map, m_mapAssets) +GET_ASSET(Cubemap, m_cubemapAssets) \ No newline at end of file diff --git a/engine/assets/include/assimpimporter.hpp b/engine/assets/include/assimpimporter.hpp new file mode 100644 index 0000000..9652bc0 --- /dev/null +++ b/engine/assets/include/assimpimporter.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include "types.hpp" + +//stub class for assimp +//TODO: this should really be merged with modelimporter.hpp +class AssimpImporter +{ +public: + static ImportedModel Import(const std::string& path); +}; \ No newline at end of file diff --git a/engine/assets/include/cubemap.hpp b/engine/assets/include/cubemap.hpp new file mode 100644 index 0000000..bda6427 --- /dev/null +++ b/engine/assets/include/cubemap.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include +#include +#include + +class Cubemap : public Asset +{ +public: + Cubemap(const std::string& path) + { + m_filepath = path; + + LoadCube(); + } + + void Load(Renderer* renderer) override; + void Unload(Renderer* renderer) override; + + void LoadCube() + { + int size; + File handle = Filesystem::GetFileHandle(m_filepath, size); + if(handle != nullptr) + { + nlohmann::json file = nlohmann::json::parse(handle); + + faceIDs[0] = file["face0"]; + faceIDs[1] = file["face1"]; + faceIDs[2] = file["face2"]; + faceIDs[3] = file["face3"]; + faceIDs[4] = file["face4"]; + faceIDs[5] = file["face5"]; + + Filesystem::FreeFileHandle(handle); + } + else + { + if(!Filesystem::IsPacked()) + { + Log::Error("Cubemap file %s not found! Creating new one...", m_filepath.c_str()); + + SaveCube(); + LoadCube(); + } + } + } + + void SaveCube() + { + nlohmann::json file; + + file["face0"] = faceIDs[0]; + file["face1"] = faceIDs[1]; + file["face2"] = faceIDs[2]; + file["face3"] = faceIDs[3]; + file["face4"] = faceIDs[4]; + file["face5"] = faceIDs[5]; + + std::ofstream out; + out.open(m_filepath); + + out << file.dump(1); + out.close(); + } + + std::string GetName() override + { + return Utility::GetFilename(m_filepath); + } + + std::string GetPath() override + { + return m_filepath; + } + + std::type_index GetType() override + { + return typeid(Cubemap); + } + + std::vector GetDependencies() override + { + return { faceIDs[0], faceIDs[1], faceIDs[2], faceIDs[3], faceIDs[4], faceIDs[5] }; + } + + int faceIDs[6]; + +private: + std::string m_filepath; +}; \ No newline at end of file diff --git a/engine/assets/include/map.hpp b/engine/assets/include/map.hpp new file mode 100644 index 0000000..c779322 --- /dev/null +++ b/engine/assets/include/map.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +class Map : public Asset +{ +public: + Map(const std::string& path); + + nlohmann::json GetContents() + { + return m_contents; + } + + void SetContents(nlohmann::json contents) + { + m_contents = contents; + } + + void SaveToFile() + { + std::ofstream out; + out.open(m_filepath); + + out << m_contents.dump(1); + out.close(); + } + + std::string GetName() override + { + return Utility::GetFilename(m_filepath); + } + + std::string GetPath() override + { + return m_filepath; + } + + std::type_index GetType() override + { + return typeid(Map); + } + +private: + nlohmann::json m_contents; + + std::string m_filepath; +}; \ No newline at end of file diff --git a/engine/assets/include/material.hpp b/engine/assets/include/material.hpp new file mode 100644 index 0000000..5ebfffe --- /dev/null +++ b/engine/assets/include/material.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Material : public Asset +{ +public: + Material(const std::string& path) + { + m_filepath = path; + + LoadMat(); + } + + void LoadMat() + { + int size; + File handle = Filesystem::GetFileHandle(m_filepath, size); + if(handle != nullptr) + { + nlohmann::json file = nlohmann::json::parse(handle); + + albedoID = file["albedo_tex"]; + albedoColor = Utility::StringToVec3(file["albedo_color"]); + normalID = file["normal_tex"]; + metallic = file["metallic"]; + roughness = file["roughness"]; + metallicID = file["metallic_tex"]; + roughnessID = file["roughness_tex"]; + emissionColor = Utility::StringToVec3(file["emission_color"]); + + Filesystem::FreeFileHandle(handle); + } + else + { + if(!Filesystem::IsPacked()) + { + Log::Error("Material file %s not found! Creating new one...", m_filepath.c_str()); + + SaveMat(); + LoadMat(); + } + } + } + + void SaveMat() + { + nlohmann::json file; + + file["albedo_tex"] = albedoID; + file["albedo_color"] = Utility::Vec3ToString(albedoColor); + file["normal_tex"] = normalID; + file["metallic"] = metallic; + file["roughness"] = roughness; + file["metallic_tex"] = metallicID; + file["roughness_tex"] = roughnessID; + file["emission_color"] = Utility::Vec3ToString(emissionColor); + + std::ofstream out; + out.open(m_filepath); + + out << file.dump(1); + out.close(); + } + + std::string GetName() override + { + return Utility::GetFilename(m_filepath); + } + + std::string GetPath() override + { + return m_filepath; + } + + std::vector GetDependencies() override + { + return { albedoID, normalID, metallicID, roughnessID }; + } + + std::type_index GetType() override + { + return typeid(Material); + } + + //used for apis like vulkan where materials are cached, useful for the editor + bool invalidated = false; + + int albedoID = 0; + glm::vec3 albedoColor = glm::vec3(1); + glm::vec3 emissionColor = glm::vec3(0); + + int normalID = 0; + + float metallic = 0.0f, roughness = 0.0f; + int metallicID = 0; + int roughnessID = 0; + +private: + std::string m_filepath; +}; diff --git a/engine/assets/include/mesh.hpp b/engine/assets/include/mesh.hpp new file mode 100644 index 0000000..4ea0c79 --- /dev/null +++ b/engine/assets/include/mesh.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "model.hpp" + +class Mesh : public Asset +{ +public: + Mesh(int index) + { + m_index = index; + } + + void Load(Renderer* renderer) override; + void Unload(Renderer* renderer) override; + + std::string GetName() override + { + return m_parentAsset->GetName() + " (" + std::to_string(m_index) + ")"; + } + + std::type_index GetType() override + { + return typeid(Mesh); + } + + int GetIndex() + { + return m_index; + } + + std::vector& GetVertices() + { + return static_cast(m_parentAsset)->submeshes[m_index].vertices; + } + + std::vector& GetIndices() + { + return static_cast(m_parentAsset)->submeshes[m_index].indices; + } + +private: + int m_index = 0; +}; \ No newline at end of file diff --git a/engine/assets/include/model.hpp b/engine/assets/include/model.hpp new file mode 100644 index 0000000..62495bd --- /dev/null +++ b/engine/assets/include/model.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +#include "modelimporter.hpp" + +class Model : public Asset +{ +public: + Model(const std::string& path) + { + m_filepath = path; + } + + void Load(Renderer*) override + { + ImportedModel imported = ModelImporter::Import(m_filepath); + submeshes = imported.submeshes; + submaterials = imported.submaterials; + + m_loaded = true; + } + + void Unload(Renderer*) override + { + submeshes.clear(); + submaterials.clear(); + + m_loaded = false; + } + + std::string GetName() override + { + return Utility::GetFilename(m_filepath); + } + + std::string GetPath() override + { + return m_filepath; + } + + std::type_index GetType() override + { + return typeid(Model); + } + + std::vector submeshes; + std::vector submaterials; + +private: + std::string m_filepath; +}; \ No newline at end of file diff --git a/engine/assets/include/modelimporter.hpp b/engine/assets/include/modelimporter.hpp new file mode 100644 index 0000000..c16e8c8 --- /dev/null +++ b/engine/assets/include/modelimporter.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include +#include + +#include "assimpimporter.hpp" +#include + +//TODO: remove the need for pointer magic in order to parse model files +class ModelImporter +{ +public: + static ImportedModel Import(const std::string& path) + { + //if the filesystem isn't packed, that means the model files aren't converted either + if(!Filesystem::IsPacked()) + { + return AssimpImporter::Import(path); + } + else + { + int size; + File handle = Filesystem::GetFileHandle(Utility::RemoveFileExtension(path) + ".model", size); + if(!handle) + { + Log::Error("Failed to open %s", (Utility::RemoveFileExtension(path) + ".model").c_str()); + return ImportedModel(); + } + + std::vector submeshes; + + const char* signature = "GRAPT"; + + char* fileSignature = new char[6]; + memcpy(fileSignature, handle, 6); + if(strcmp(signature, fileSignature)) + { + Log::Error("Invalid signature!"); + return ImportedModel(); + } + + handle += sizeof(signature); + + unsigned int numMeshes; + memcpy(&numMeshes, handle, sizeof(unsigned int)); + + handle += sizeof(unsigned int); + + const char* terminator = "m"; + + for(unsigned int i = 0; i < numMeshes; i++) + { + Submesh mesh; + + handle += sizeof(terminator); + + unsigned int numVerts; + memcpy(&numVerts, handle, sizeof(unsigned int)); + + handle += sizeof(unsigned int); + + unsigned int numIndices; + memcpy(&numIndices, handle, sizeof(unsigned int)); + + handle += sizeof(unsigned int); + + for(unsigned int v = 0; v < numVerts; v++) + { + Vertex vertex; + memcpy(&vertex, handle, sizeof(Vertex)); + + handle += sizeof(Vertex); + + mesh.vertices.push_back(vertex); + } + + for(unsigned int e = 0; e < numIndices; e++) + { + unsigned int indice; + memcpy(&indice, handle, sizeof(unsigned int)); + + handle += sizeof(unsigned int); + + mesh.indices.push_back(indice); + } + + submeshes.push_back(mesh); + } + + ImportedModel model; + model.submeshes = submeshes; + + return model; + } + } + + static void Export(const std::string& inpath, const std::string& outpath) + { + auto submeshes = Import(inpath).submeshes; + + FILE* fp = fopen(outpath.c_str(), "wb"); + if(!fp) + { + Log::Error("Failed to open file %s", outpath.c_str()); + return; + } + + const char* signature = "GRAPT"; //signature used to check if they are our models + fwrite(signature, sizeof(signature), 1, fp); + + const char* terminator = "m"; //terminator used for splitting meshes + + unsigned int numMeshes = submeshes.size(); + fwrite(&numMeshes, sizeof(unsigned int), 1, fp); + + for(unsigned int i = 0; i < submeshes.size(); i++) + { + Submesh mesh = submeshes[i]; + + //write a terminator to separate meshes + fwrite(terminator, sizeof(terminator), 1, fp); + + unsigned int numVertices = mesh.vertices.size(); + fwrite(&numVertices, sizeof(unsigned int), 1, fp); + + unsigned int numIndices = mesh.indices.size(); + fwrite(&numIndices, sizeof(unsigned int), 1, fp); + + for(unsigned int v = 0; v < mesh.vertices.size(); v++) + { + fwrite(&mesh.vertices[v], sizeof(Vertex), 1, fp); + } + + for(unsigned int e = 0; e < mesh.indices.size(); e++) + { + fwrite(&mesh.indices[e], sizeof(unsigned int), 1, fp); + } + } + + fclose(fp); + } +}; \ No newline at end of file diff --git a/engine/assets/include/skeleton.hpp b/engine/assets/include/skeleton.hpp new file mode 100644 index 0000000..e929c49 --- /dev/null +++ b/engine/assets/include/skeleton.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +constexpr int MAX_BONES_PER_VERTEX = 4; + +struct VertexBoneData +{ + std::array ids; + std::array weights; + + // Ad bone weighting to vertex info + void add(uint32_t boneID, float weight) + { + for(uint32_t i = 0; i < MAX_BONES_PER_VERTEX; i++) + { + if(weights[i] == 0.0f) + { + ids[i] = boneID; + weights[i] = weight; + return; + } + } + } +}; + +struct BoneInfo +{ + glm::mat4 offset, final; +}; + +struct aiNode; +struct aiScene; + +class Skeleton : public Asset +{ +public: + Skeleton(int i) + { + index = i; + } + + void Load(Renderer* renderer) override; + void Unload(Renderer* renderer) override; + + std::type_index GetType() override + { + return typeid(Skeleton); + } + + void UpdateSkeletalNodes(aiNode* pNode, const glm::mat4& parentTransformation); + + std::vector vertex_bones; + std::map mapping; + std::vector bone_info; + + glm::mat4 globalInverse; + aiScene* animScene; + + float animTime = 0.0f; + + int index; +}; diff --git a/engine/assets/include/texture.hpp b/engine/assets/include/texture.hpp new file mode 100644 index 0000000..d9c2be4 --- /dev/null +++ b/engine/assets/include/texture.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class Texture : public Asset +{ +public: + Texture(const std::string& path) + { + filepath = path; + } + + void Load(Renderer* renderer) override; + void Unload(Renderer* renderer) override; + + std::string GetName() override + { + return Utility::GetFilename(filepath); + } + + std::string GetPath() override + { + return filepath; + } + + std::type_index GetType() override + { + return typeid(Texture); + } + + unsigned char* pixels; + int height, width, channels; + +private: + std::string filepath; +}; diff --git a/engine/assets/include/types.hpp b/engine/assets/include/types.hpp new file mode 100644 index 0000000..c713a7c --- /dev/null +++ b/engine/assets/include/types.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +struct Vertex +{ + glm::vec3 position, normal; + glm::vec2 uv; + glm::vec3 tangent; +}; + +struct Submesh +{ + std::vector vertices; + std::vector indices; + + int materialIndex; +}; + +struct Submaterial +{ + std::string name; + + glm::vec3 diffusecolor; + std::string diffuseTexture; +}; + +struct ImportedModel +{ + std::vector submeshes; + std::vector submaterials; +}; diff --git a/engine/assets/src/assetmanager.cpp b/engine/assets/src/assetmanager.cpp new file mode 100644 index 0000000..e077d12 --- /dev/null +++ b/engine/assets/src/assetmanager.cpp @@ -0,0 +1,571 @@ +#include "assetmanager.hpp" + +#include +#include + +std::map AssetManager::m_filepaths; +std::map AssetManager::m_modelAssets; +std::map AssetManager::m_meshAssets; +std::map AssetManager::m_materialAssets; +std::map AssetManager::m_textureAssets; +std::map AssetManager::m_skeletonAssets; +std::map AssetManager::m_mapAssets; +std::map AssetManager::m_cubemapAssets; + +EventDispatcher AssetManager::onAssetListUpdate; + +Renderer* AssetManager::m_renderer; + +void AssetManager::SetRenderer(Renderer* renderer) +{ + m_renderer = renderer; +} + +void AssetManager::SearchDisk(const std::string& path) +{ + if(!Filesystem::DirectoryExists(path)) + { + Log::Error("Search path doesnt exist!"); + return; + } + + auto entries = Filesystem::DirectoryContents(path); + for(auto p : entries) + { + if(p.find(".meta") != std::string::npos) + LoadMeta(p); + } +} + +void AssetManager::SearchPacked() +{ + int size; + File handle = Filesystem::GetFileHandle("index.txt", size); + if(handle != nullptr) + { + std::istringstream iss(handle); + for(std::string line; std::getline(iss, line);) + LoadMeta(line); + } + else + { + Log::Error("Could not find assets index!"); + } +} + +void AssetManager::LoadMeta(const std::string& path) +{ + DEBUG("Loading meta file: %s", path.c_str()) + + nlohmann::json file; + int size; + File handle = Filesystem::GetFileHandle(path, size); + if(!handle) + { + Log::Error("Failed to read meta file %s!", path.c_str()); + return; + } + + try + { + file = nlohmann::json::parse(std::string(handle)); + } + catch (std::exception &exception) + { + Log::Error("Failed to parse meta file %s (%s)", path.c_str(), exception.what()); + return; + } + + ParseMeta(file, path); + + onAssetListUpdate(); +} + +void AssetManager::ParseMeta(nlohmann::json file, std::string path, Asset* parentAsset) +{ + int id = file["id"]; + std::string type = file["type"]; + + if(type == "model") + { + std::string filetype = file["filetype"]; + std::string filepath = path.substr(0, path.find_last_of(".")) + "." + filetype; + + Model* modelAsset = new Model(filepath); + + m_modelAssets.emplace(id, modelAsset); + m_filepaths.emplace(filepath, id); + + nlohmann::json children = file["children"]; + for(auto child : children) + ParseMeta(child, path, modelAsset); + } + else if(type == "mesh") + { + Mesh* meshAsset = new Mesh(static_cast(file["index"])); + meshAsset->SetParent(parentAsset); + + m_meshAssets.emplace(id, meshAsset); + } + else if(type == "material") + { + std::string filepath = path.substr(0, path.find_last_of(".")) + ".vmat"; + + //use the path if given (for example from a model) + if(file.count("path")) + filepath = file["path"]; + + Material* materialAsset = new Material(filepath); + + m_materialAssets.emplace(id, materialAsset); + m_filepaths.emplace(filepath, id); + } + else if(type == "texture") + { + std::string filetype = file["filetype"]; + std::string filepath = path.substr(0, path.find_last_of(".")) + "." + filetype; + + Texture* textureAsset = new Texture(filepath); + + m_textureAssets.emplace(id, textureAsset); + m_filepaths.emplace(filepath, id); + } + else if(type == "skeleton") + { + Skeleton* skeletonAsset = new Skeleton(static_cast(file["index"])); + skeletonAsset->SetParent(parentAsset); + + m_skeletonAssets.emplace(id, skeletonAsset); + } + else if(type == "map") + { + std::string filepath = path.substr(0, path.find_last_of(".")) + ".vmap"; + + Map* mapAsset = new Map(filepath); + mapAsset->SetParent(parentAsset); + + m_mapAssets.emplace(id, mapAsset); + m_filepaths.emplace(filepath, id); + } + else if(type == "cubemap") + { + std::string filepath = path.substr(0, path.find_last_of(".")) + ".vcube"; + + if(file.count("path")) + filepath = file["path"]; + + Cubemap* cubemapAsset = new Cubemap(filepath); + cubemapAsset->SetParent(parentAsset); + + m_cubemapAssets.emplace(id, cubemapAsset); + m_filepaths.emplace(filepath, id); + } +} + +nlohmann::json AssetManager::ConstructMeta(Asset *asset, const int id) +{ + std::type_index type = asset->GetType(); + + std::string filepath = asset->GetPath().substr(0, asset->GetPath().find_last_of(".")) + ".meta"; + Log::Print("Saving meta %s", filepath.c_str()); + + nlohmann::json file; + file["id"] = id; + + if(type == typeid(Model)) + { + file["type"] = "model"; + file["filetype"] = asset->GetPath().substr(asset->GetPath().find_last_of(".") + 1, asset->GetPath().length()); + } + else if(type == typeid(Mesh)) + { + Mesh* mesh = static_cast(asset); + + file["type"] = "mesh"; + file["index"] = mesh->GetIndex(); + } + else if(type == typeid(Texture)) + { + file["type"] = "texture"; + file["filetype"] = asset->GetPath().substr(asset->GetPath().find_last_of(".") + 1, asset->GetPath().length()); + } + else if(type == typeid(Material)) + { + //if we are material from a model + if(asset->GetParent() != nullptr) + file["path"] = static_cast(asset)->GetPath(); + + file["type"] = "material"; + } + else if(type == typeid(Skeleton)) + { + Skeleton* skeleton = static_cast(asset); + + file["type"] = "skeleton"; + file["index"] = skeleton->index; + } + else if(type == typeid(Map)) + { + Map* map = static_cast(asset); + + file["type"] = "map"; + } + else if(type == typeid(Cubemap)) + { + Cubemap* map = static_cast(asset); + + file["type"] = "cubemap"; + } + + auto assetchildren = GetChildren(asset); + if(assetchildren.size() > 0) + { + nlohmann::json &children = file["children"]; + + for(auto& iter : assetchildren) + children.push_back(ConstructMeta(iter.second, iter.first)); + } + + return file; +} + +void AssetManager::SaveMeta(const int id) +{ + Asset* asset = GetAsset(id); + + nlohmann::json file = ConstructMeta(asset, id); + + std::string filepath = asset->GetPath().substr(0, asset->GetPath().find_last_of(".")) + ".meta"; + + Log::Print("Saving meta file %s", filepath.c_str()); + + std::ofstream out(filepath); + if(out.is_open()) + { + out << file.dump(1); + out.close(); + } else { + Log::Error("Unable to write meta file to %s", filepath.c_str()); + } +} + +std::vector AssetManager::GetDependencies(Asset* asset) +{ + std::vector tmp; + + auto ids = asset->GetDependencies(); + for(auto id : ids) + { + Asset* idAsset = GetAsset(id); + if(idAsset != nullptr) + tmp.push_back(idAsset); + } + + return tmp; +} + +std::map AssetManager::GetChildren(Asset* asset) +{ + std::map tmp; + + auto assets = GetAssets(); + for(auto &itr : assets) + if(itr.second->GetParent() == asset && itr.second != nullptr) + tmp.emplace(itr); + + return tmp; +} + +void AssetManager::LoadAssets(const std::vector& ids) +{ + for(auto id : ids) + { + Asset* asset = GetAsset(id); + + if(asset != nullptr) + { + if(!asset->IsLoaded()) + asset->Load(m_renderer); + + auto dependencies = GetDependencies(asset); + for(auto dependency : dependencies) + { + if(!dependency->IsLoaded()) + dependency->Load(m_renderer); + } + } + } +} + +void AssetManager::ImportAsset(const std::string& filepath, const std::string& dest) +{ + if(filepath.length() == 0) + return; + + std::string destpath = dest + "/" + Utility::GetFilename(filepath); + + std::string extension = filepath.substr(filepath.find_last_of(".") + 1, filepath.length()); + Log::Print("Copying asset %s to %s", filepath.c_str(), dest.c_str()); + + Filesystem::CopyFile(filepath, destpath); + + if(extension == "obj" || extension == "fbx" || extension == "md5mesh" || extension == "dae") + { + std::string mtlfile = filepath.substr(0, filepath.find_last_of(".")) + ".mtl"; + + //if a mtl exists, copy it too + if(Filesystem::FileExists(mtlfile)) + Filesystem::CopyFile(mtlfile, destpath.substr(0, destpath.find_last_of(".")) + ".mtl"); + + ImportModel(destpath); + } + else if(extension == "png" || extension == "jpg" || extension == "bmp") + { + ImportTexture(destpath); + } + else + { + Log::Print("Unknown asset file type %s", extension.c_str()); + } +} + +void AssetManager::ImportModel(const std::string& filepath) +{ + Log::Print("Importing model %s", filepath.c_str()); + + Model* modelAsset = new Model(filepath); + int pid = Utility::Random(1, 10000); + + m_modelAssets.emplace(pid, modelAsset); + m_filepaths.emplace(filepath, pid); + + modelAsset->Load(m_renderer); + + for(unsigned int i = 0; i < modelAsset->submeshes.size(); i++) + { + Mesh* meshAsset = new Mesh(i); + meshAsset->SetParent(modelAsset); + + m_meshAssets.emplace(Utility::Random(1, 10000), meshAsset); + + meshAsset->Load(m_renderer); + } + + for(unsigned int i = 0; i < modelAsset->submaterials.size(); i++) + { + Material* materialAsset = new Material(Utility::RemoveToLastSlash(filepath) + "/" + modelAsset->submaterials[i].name + ".vmat"); + materialAsset->SetParent(modelAsset); + + materialAsset->albedoColor = modelAsset->submaterials[i].diffusecolor; + + std::cout << modelAsset->submaterials[i].diffuseTexture << std::endl; + + std::string texturePath = Utility::RemoveToLastSlash(filepath) + "/" + modelAsset->submaterials[i].diffuseTexture; + + std::cout << texturePath << std::endl; + + //if texture exists in this folder, also import it + //TODO: add a conflict/import resolution dialog + if(Filesystem::FileExists(texturePath)) + { + materialAsset->albedoID = GetID(ImportTexture(texturePath)); + } + + int id = Utility::Random(1, 10000); + + m_materialAssets.emplace(id, materialAsset); + m_filepaths.emplace(filepath, id); + + materialAsset->Load(m_renderer); + } + + SaveMeta(pid); + + onAssetListUpdate(); +} + +Texture* AssetManager::ImportTexture(const std::string& filepath) +{ + Log::Print("Importing texture %s", filepath.c_str()); + + Texture* textureAsset = new Texture(filepath); + int id = Utility::Random(1, 10000); + + m_textureAssets.emplace(id, textureAsset); + m_filepaths.emplace(filepath, id); + + SaveMeta(id); + + onAssetListUpdate(); + + return textureAsset; +} + +void AssetManager::DeleteAsset(Asset* asset) +{ + Log::Print("Deleting asset %s...", asset->GetName().c_str()); + + int id = GetID(asset); + + auto children = GetChildren(asset); + for(auto child : children) + DeleteAsset(child.second); + + std::string filepath = asset->GetPath().substr(0, asset->GetPath().find_last_of(".")) + ".meta"; + + Filesystem::Remove(filepath); + Filesystem::Remove(asset->GetPath()); + + Utility::RemoveFromMap(m_filepaths, asset->GetPath()); + Utility::RemoveFromMap(m_modelAssets, id); + Utility::RemoveFromMap(m_meshAssets, id); + Utility::RemoveFromMap(m_materialAssets, id); + Utility::RemoveFromMap(m_textureAssets, id); + Utility::RemoveFromMap(m_skeletonAssets, id); + Utility::RemoveFromMap(m_mapAssets, id); + Utility::RemoveFromMap(m_cubemapAssets, id); + + asset->Unload(m_renderer); + delete asset; + + onAssetListUpdate(); +} + +Material* AssetManager::CreateMaterial(const std::string& filepath) +{ + Log::Print("Creating material %s", filepath.c_str()); + + Material* materialAsset = new Material(filepath); + int id = Utility::Random(1, 10000); + + m_materialAssets.emplace(id, materialAsset); + m_filepaths.emplace(filepath, id); + + SaveMeta(id); + + onAssetListUpdate(); + + return materialAsset; +} + +Cubemap* AssetManager::CreateCubemap(const std::string& filepath) +{ + Log::Print("Creating cubemap %s", filepath.c_str()); + + Cubemap* cubemapAsset = new Cubemap(filepath); + int id = Utility::Random(1, 10000); + + m_cubemapAssets.emplace(id, cubemapAsset); + m_filepaths.emplace(filepath, id); + + SaveMeta(id); + + onAssetListUpdate(); + + return cubemapAsset; +} + +Map* AssetManager::CreateMap(World* world, const std::string& filepath) +{ + Log::Print("Creating map %s", filepath.c_str()); + + Map* mapAsset = new Map(filepath); + mapAsset->SetContents(world->DumpToJSON()); + int id = Utility::Random(1, 10000); + + mapAsset->SaveToFile(); + + m_mapAssets.emplace(id, mapAsset); + m_filepaths.emplace(filepath, id); + + SaveMeta(id); + + onAssetListUpdate(); + + return mapAsset; +} + +std::vector AssetManager::GetDirectoryAssets(const std::string& directory) +{ + std::vector tmp; + for(auto path : m_filepaths) + { + std::string p = Filesystem::Canonical(path.first); + std::string fp = p.substr(0, p.find_last_of("/\\")); + + if(directory == fp) + tmp.push_back(GetAsset(path.second)); + } + + return tmp; +} + +void AssetManager::Cleanup() +{ + for(auto& itr : m_textureAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_meshAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_modelAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_skeletonAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_materialAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_mapAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + for(auto& itr : m_cubemapAssets) + { + if(itr.second->IsLoaded()) + itr.second->Unload(m_renderer); + + delete itr.second; + } + + m_filepaths.clear(); + m_textureAssets.clear(); + m_meshAssets.clear(); + m_modelAssets.clear(); + m_skeletonAssets.clear(); + m_materialAssets.clear(); + m_mapAssets.clear(); + m_cubemapAssets.clear(); + + Log::Print("Cleaned up assets!"); +} diff --git a/engine/assets/src/cubemap.cpp b/engine/assets/src/cubemap.cpp new file mode 100644 index 0000000..622f0c1 --- /dev/null +++ b/engine/assets/src/cubemap.cpp @@ -0,0 +1,13 @@ +#include "cubemap.hpp" + +#include + +void Cubemap::Load(Renderer* renderer) +{ + renderer->SynthesizeCubemap(this); +} + +void Cubemap::Unload(Renderer* renderer) +{ + renderer->ReleaseCubemap(this); +} \ No newline at end of file diff --git a/engine/assets/src/map.cpp b/engine/assets/src/map.cpp new file mode 100644 index 0000000..e4ba620 --- /dev/null +++ b/engine/assets/src/map.cpp @@ -0,0 +1,42 @@ +#include "map.hpp" + +#include +#include +#include +#include + +Map::Map(const std::string& path) +{ + if(path == "") + { + Log::Print("Invalid map path! Creating empty map..."); + return; + } + + m_filepath = path; + + Log::Print("Loading map: %s", m_filepath.c_str()); + + int size; + File handle = Filesystem::GetFileHandle(m_filepath, size); + if(handle == nullptr) + { + Log::Error("Failed to read map file!"); + return; + } + + try + { + m_contents = nlohmann::json::parse(handle); + + Filesystem::FreeFileHandle(handle); + DrawList::InvalidateNextFrame(); + } + catch(std::exception& exception) + { + Log::Error("Failed to parse map file! (%s)", exception.what()); + + Filesystem::FreeFileHandle(handle); + return; + } +} \ No newline at end of file diff --git a/engine/assets/src/mesh.cpp b/engine/assets/src/mesh.cpp new file mode 100644 index 0000000..57bdeae --- /dev/null +++ b/engine/assets/src/mesh.cpp @@ -0,0 +1,20 @@ +#include "mesh.hpp" + +#include + +void Mesh::Load(Renderer* renderer) +{ + if(!m_parentAsset->IsLoaded()) + m_parentAsset->Load(renderer); + + renderer->SynthesizeMesh(this); + + m_loaded = true; +} + +void Mesh::Unload(Renderer* renderer) +{ + renderer->ReleaseMesh(this); + + m_loaded = false; +} \ No newline at end of file diff --git a/engine/assets/src/skeleton.cpp b/engine/assets/src/skeleton.cpp new file mode 100644 index 0000000..50f45bf --- /dev/null +++ b/engine/assets/src/skeleton.cpp @@ -0,0 +1,20 @@ +#include "skeleton.hpp" + +#include + +void Skeleton::Load(Renderer* renderer) +{ + if(!m_parentAsset->IsLoaded()) + m_parentAsset->Load(renderer); + + m_loaded = true; +} + +void Skeleton::Unload(Renderer*) +{ + vertex_bones.clear(); + mapping.clear(); + bone_info.clear(); + + m_loaded = false; +} \ No newline at end of file diff --git a/engine/assets/src/texture.cpp b/engine/assets/src/texture.cpp new file mode 100644 index 0000000..2265cc8 --- /dev/null +++ b/engine/assets/src/texture.cpp @@ -0,0 +1,34 @@ +#include "texture.hpp" + +#define STB_IMAGE_IMPLEMENTATION +#include + +#include +#include + +void Texture::Load(Renderer* renderer) +{ + int size; + File handle = Filesystem::GetFileHandle(filepath, size); + if(handle != nullptr) + { + pixels = stbi_load_from_memory(reinterpret_cast(handle), size, &width, &height, &channels, STBI_rgb_alpha); + + if(pixels != nullptr) + { + renderer->SynthesizeTexture(this); + + Filesystem::FreeFileHandle(handle); + m_loaded = true; + } + } +} + +void Texture::Unload(Renderer* renderer) +{ + renderer->ReleaseTexture(this); + + stbi_image_free(pixels); + + m_loaded = false; +} \ No newline at end of file diff --git a/engine/core/CMakeLists.txt b/engine/core/CMakeLists.txt new file mode 100644 index 0000000..59bf070 --- /dev/null +++ b/engine/core/CMakeLists.txt @@ -0,0 +1,31 @@ +set(SOURCE_FILES + src/engine.cpp + src/world.cpp + src/gameinstance.cpp + src/scriptsystem.cpp + src/physicssystem.cpp) + +add_library(Core ${SOURCE_FILES}) + +target_link_libraries(Core Platform Assets ECS ImGui Input Angelscript Renderer BulletDynamics BulletCollision LinearMath) + +include_directories(include) + +include_directories( + ${RENDERER_INCLUDE_DIR} + ${ASSETS_INCLUDE_DIR} + ${ECS_INCLUDE_DIR} + ${UTILITY_INCLUDE_DIR} + ${INPUT_INCLUDE_DIR} + ${PLATFORM_INCLUDE_DIR}) + +include_directories(SYSTEM + ${LIBRARY_JSON_INCLUDE_DIR} + ${LIBRARY_IMGUI_INCLUDE_DIR} + ${LIBRARY_STB_INCLUDE_DIR} + ${LIBRARY_ANGELSCRIPT_INCLUDE_DIR} + ${LIBRARY_BULLET_INCLUDE_DIR} + ${LIBRARY_GLM_INCLUDE_DIR} + ${LIBRARY_VULKAN_INCLUDE_DIR} + ${LIBRARY_PHYSFS_INCLUDE_DIR} + ${LIBRARY_ASSIMP_INCLUDE_DIR}) diff --git a/engine/core/include/engine.hpp b/engine/core/include/engine.hpp new file mode 100644 index 0000000..4668470 --- /dev/null +++ b/engine/core/include/engine.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +class GameInstance; + +struct GameInfo +{ + GameInfo() + { + gameName = "Game"; + defaultMap = ""; + benchmarkingEnabled = false; + debugGUIEnabled = false; + isEditor = false; + } + + std::string gameName, defaultMap; + + bool benchmarkingEnabled; + bool debugGUIEnabled; + bool isEditor; +}; + +class Engine +{ +public: + Engine(GameInfo info); + ~Engine(); + + void Tick(bool updateSystems = true); + + template + T* GetSystem() + { + static_assert(std::is_base_of::value, "GetSystem requires to be given a type that derives from System!"); + + if(m_systems.count(typeid(T))) + return static_cast(m_systems.at(typeid(T))); + + return nullptr; + } + + Renderer* GetRenderer() const + { + return m_renderer; + } + + GameInstance* GetGame() const + { + return m_gameInstance; + } + + GameInfo gameInfo; + +private: + template + void AddSystem() + { + static_assert(std::is_base_of::value, "AddSystem requires that the type derives from System!"); + + T* t = new T; + static_cast(t)->SetEngine(this); + + m_systems.emplace(typeid(T), t); + } + + void ApplyOptions(); + + void PickSuitableBackend(); + void CreateBackend(); + + void DrawDebug(); + + bool m_debugging, m_benchmarking; + + GameInstance* m_gameInstance; + Renderer* m_renderer; + + std::map m_systems; + + //used for time calculation + std::chrono::time_point m_applicationStart; + std::chrono::duration m_currentTime; + int m_frames = 0; + float m_lastTime = 0.0f; + + int m_oldWindowX = 0, m_oldWindowY = 0; +}; \ No newline at end of file diff --git a/engine/core/include/engineconstants.hpp b/engine/core/include/engineconstants.hpp new file mode 100644 index 0000000..fb810e9 --- /dev/null +++ b/engine/core/include/engineconstants.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace constants +{ + constexpr const char* engineName = "Graphite"; + + constexpr int defaultScreenWidth = 1600; + constexpr int defaultScreenHeight = 900; + + //debug + constexpr int gridSize = 100; +} \ No newline at end of file diff --git a/engine/core/include/gameinstance.hpp b/engine/core/include/gameinstance.hpp new file mode 100644 index 0000000..0deb35f --- /dev/null +++ b/engine/core/include/gameinstance.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +class Engine; +class World; + +class GameInstance +{ +public: + GameInstance(Engine* engine); + ~GameInstance(); + + void Update(); + + void SaveOptions(); + void LoadOptions(); + + void CreateEmptyWorld(); + void CleanupWorld(); + + World* DuplicateWorld(); + + void LoadMap(const std::string& path, bool doNotCreateActors = false); + void LoadWorld(World* world); + + void CreateActors(); + + World* GetWorld() const + { + return m_world; + } + + struct + { + bool fullscreen; + bool vsync; + } options; + +private: + Engine* m_engine; + World* m_world = nullptr; + + bool m_mouselookEnabled = true; +}; \ No newline at end of file diff --git a/engine/core/include/physicssystem.hpp b/engine/core/include/physicssystem.hpp new file mode 100644 index 0000000..dc4ba6e --- /dev/null +++ b/engine/core/include/physicssystem.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +class PhysicsSystem : public System +{ +public: + void Initialize() override; + void Cleanup() override; + + void Update() override; + + bool Raycast(glm::vec3 start, glm::vec3 end); + +private: + btDbvtBroadphase* m_broadPhase; + btDefaultCollisionConfiguration* m_collisionConfiguration; + btSequentialImpulseConstraintSolver* m_solver; + btCollisionDispatcher* m_dispatcher; + + btDynamicsWorld* m_dynamicsWorld; +}; \ No newline at end of file diff --git a/engine/core/include/scriptsystem.hpp b/engine/core/include/scriptsystem.hpp new file mode 100644 index 0000000..3d26d85 --- /dev/null +++ b/engine/core/include/scriptsystem.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +class Entity; + +class ScriptSystem : public System +{ +public: + void Initialize() override; + void Cleanup() override; + + void Update() override; + +private: + void RunFunction(const std::string& name, const std::string& module_name); + + Entity* GetEntity(); + + asIScriptEngine* m_scriptEngine; +}; \ No newline at end of file diff --git a/engine/core/include/system.hpp b/engine/core/include/system.hpp new file mode 100644 index 0000000..9711083 --- /dev/null +++ b/engine/core/include/system.hpp @@ -0,0 +1,22 @@ +#pragma once + +class Engine; + +class System +{ +public: + virtual ~System() {} + + void SetEngine(Engine* engine) + { + m_engine = engine; + } + + virtual void Initialize() {} + virtual void Cleanup() {} + + virtual void Update() {} + +protected: + Engine* m_engine; +}; \ No newline at end of file diff --git a/engine/core/include/world.hpp b/engine/core/include/world.hpp new file mode 100644 index 0000000..57a6c11 --- /dev/null +++ b/engine/core/include/world.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +class GameInstance; +class Entity; +class Map; + +/* + * Worlds are a collection of entities - but they only reference the entity's ids so they are technically still held by + * the entity pool. + */ +class World +{ + friend class GameInstance; +public: + World(GameInstance* gameInstance) : m_gameInstance(gameInstance) {} + ~World(); + + void LoadFromMap(Map* map); + + void AddEntity(Entity* entity); + void AddEntity(int id); + + nlohmann::json DumpToJSON(); + + std::string GetPath() + { + return m_path; + } + + int skyboxID = 0; + +protected: + std::vector m_associatedEntities; + +private: + std::string m_path; + GameInstance* m_gameInstance; +}; \ No newline at end of file diff --git a/engine/core/src/engine.cpp b/engine/core/src/engine.cpp new file mode 100644 index 0000000..fefc5a3 --- /dev/null +++ b/engine/core/src/engine.cpp @@ -0,0 +1,135 @@ +#include "engine.hpp" + +#include +#include +#include +#include +#include +#include + +#include "gameinstance.hpp" +#include "scriptsystem.hpp" +#include "physicssystem.hpp" +#include "time.hpp" + +Engine::Engine(GameInfo info) : gameInfo(info) +{ + m_benchmarking = info.benchmarkingEnabled; + m_debugging = info.debugGUIEnabled; + + RequestedFeatures features; + features.imgui = m_debugging; + features.debugPass = info.isEditor; + features.autoload = info.isEditor; + + m_renderer = new Renderer(this, features); + + AssetManager::SetRenderer(m_renderer); + + Filesystem::IsPacked() ? AssetManager::SearchPacked() : AssetManager::SearchDisk("."); + + m_gameInstance = new GameInstance(this); + m_gameInstance->LoadOptions(); + + ApplyOptions(); + + AddSystem(); + AddSystem(); + + for(auto& system : m_systems) + system.second->Initialize(); + + m_applicationStart = std::chrono::high_resolution_clock::now(); +} + +Engine::~Engine() +{ + for(auto& system : m_systems) + { + system.second->Cleanup(); + delete system.second; + } + + delete m_gameInstance; + + AssetManager::Cleanup(); + + delete m_renderer; + + GetEntPool().Cleanup(); +} + +void Engine::Tick(bool updateSystems) +{ + m_currentTime = std::chrono::high_resolution_clock::now() - m_applicationStart; + + m_renderer->NewFrame(); + + //time calculations + Time::GetDeltaTime() = m_currentTime.count() - m_lastTime; + m_lastTime = m_currentTime.count(); + m_frames++; + + if(m_benchmarking) + Log::Print("fps: %f", m_frames / m_currentTime.count()); + + if(m_debugging) + DrawDebug(); + + m_gameInstance->Update(); + + if(updateSystems) + { + for(auto& system : m_systems) + system.second->Update(); + } + + m_renderer->Draw(); + + GetEntPool().ProcessDeletions(); + + Input::NewFrame(); + InputPump::PollEvents(); +} + +void Engine::ApplyOptions() +{ + m_gameInstance->options.fullscreen ? GFX::GoFullscreen() : GFX::ExitFullscreen(); + + RuntimeOptions options; + options.vsync = m_gameInstance->options.vsync; + + //m_renderer->ChangeOptions(options); +} + +void Engine::DrawDebug() +{ + ImGui::Begin("Debug Info"); + + ImGui::Text("time:"); + ImGui::Separator(); + + ImGui::Text("delta time: %f", Time::GetDeltaTime()); + ImGui::Text("fps: %f", m_frames / m_currentTime.count()); + + ImGui::Text("graphics:"); + ImGui::Separator(); + + ImGui::Text("dynamic draw calls: %lu", m_renderer->GetDrawList().GetDynamicGeometry().size()); + ImGui::Text("static draw calls: %lu", m_renderer->GetDrawList().GetStaticGeometry().drawCommands.size()); + + ImGui::End(); + + ImGui::Begin("Game Options"); + + ImGui::Checkbox("Fullscreen", &m_gameInstance->options.fullscreen); + ImGui::Checkbox("VSync", &m_gameInstance->options.vsync); + + if(ImGui::Button("Save")) + m_gameInstance->SaveOptions(); + + if(ImGui::Button("Apply")) + ApplyOptions(); + + ImGui::End(); +} \ No newline at end of file diff --git a/engine/core/src/gameinstance.cpp b/engine/core/src/gameinstance.cpp new file mode 100644 index 0000000..d95c7fb --- /dev/null +++ b/engine/core/src/gameinstance.cpp @@ -0,0 +1,252 @@ +#include "gameinstance.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "physicssystem.hpp" +#include "engine.hpp" +#include "map.hpp" +#include "world.hpp" + +GameInstance::GameInstance(Engine* engine) : m_engine(engine) +{ + Log::Print("Creating game instance..."); + + if(m_engine->gameInfo.defaultMap != "") + LoadMap(m_engine->gameInfo.defaultMap); +} + +GameInstance::~GameInstance() +{ + if(m_world) + CleanupWorld(); +} + +void GameInstance::Update() +{ + auto playerCameras = GetEntPool().GetOf(); + for(auto& playerCamera : playerCameras) + { + if(Input::IsKeyPressed(Key::J)) + m_mouselookEnabled = !m_mouselookEnabled; + + if(m_mouselookEnabled) + { + double x, y; + Input::GetMousePosition(x, y); + + float xoffset = ((GFX::GetScreenWidth() / 2.0f) - x); + float yoffset = ((GFX::GetScreenHeight() / 2.0f) - y); + + Platform::WarpMouse(GFX::GetScreenWidth() / 2.0f, GFX::GetScreenHeight() / 2.0f); + + xoffset *= 0.01f; + yoffset *= 0.01f; + + if(xoffset != playerCamera->lastxoffset) + playerCamera->yaw += xoffset; + + if(yoffset != playerCamera->lastyoffset) + playerCamera->pitch += yoffset; + + playerCamera->lastxoffset = xoffset; + playerCamera->lastyoffset = yoffset; + + playerCamera->yaw = Utility::Lerp(playerCamera->lastYaw, playerCamera->yaw, 0.5f); + playerCamera->pitch = Utility::Lerp(playerCamera->lastPitch, playerCamera->pitch, 0.5f); + + //playerCamera->pitch = Utility::Clip(playerCamera->pitch, 75.7f, 78.0f); + + playerCamera->GetEntity()->GetTransform()->orientation = glm::lerp(playerCamera->GetEntity()->GetTransform()->orientation, + glm::angleAxis(playerCamera->yaw, glm::vec3(0, 1, 0)) * + glm::angleAxis(playerCamera->pitch, glm::vec3(1, 0, 0)), 0.5f); + + playerCamera->lastYaw = playerCamera->yaw; + playerCamera->lastPitch = playerCamera->pitch; + } + } + + auto players = GetEntPool().GetOf(); + for(auto& player : players) + { + if(player->GetEntity()->Get()->rigidbody == nullptr) + return; + + Entity* playerCamera = player->GetEntity()->GetTransform()->GetChildren()[0]->GetEntity(); + + glm::vec3 direction; + glm::vec3 forward = glm::normalize(playerCamera->GetTransform()->GetForward()); + glm::vec3 right = glm::normalize(playerCamera->GetTransform()->GetRight()); + + if(Input::IsKeyDown(Key::W)) + direction -= forward * 15.0f; + + if(Input::IsKeyDown(Key::A)) + direction -= right * 15.0f; + + if(Input::IsKeyDown(Key::D)) + direction += right * 15.0f; + + if(Input::IsKeyDown(Key::S)) + direction += forward * 15.0f; + + direction = glm::lerp(player->previousDirection, direction, 0.5f); + + player->previousDirection = direction; + + glm::vec3 start, end; + start = player->GetEntity()->GetTransform()->position; + end = glm::vec3(start.x, start.y - 3.4f, start.z); + + //check if the player is grounded or not + if(!m_engine->GetSystem()->Raycast(start, end)) + { + direction.y = -9.8f; + } + else + { + direction.y = 0.0f; + } + + btVector3 newVelocity = btVector3(direction.x, direction.y, direction.z); + + player->GetEntity()->Get()->rigidbody->setLinearVelocity(newVelocity); + } +} + +void GameInstance::CreateEmptyWorld() +{ + if(m_world) + CleanupWorld(); + + m_world = new World(this); +} + +void GameInstance::LoadMap(const std::string& path, bool doNotCreateActors) +{ + Map* map = AssetManager::GetByName(path); + if(map == nullptr) + { + Log::Error("Not an actual map %s!", path.c_str()); + } + else + { + if(m_world) + CleanupWorld(); + + m_world = new World(this); + m_world->LoadFromMap(map); + + if(!doNotCreateActors) + CreateActors(); + } +} + +void GameInstance::LoadWorld(World* world) +{ + if(m_world) + CleanupWorld(); + + m_world = world; + +} + +void GameInstance::CleanupWorld() +{ + delete m_world; + m_world = nullptr; + + auto players = GetEntPool().GetOf(); + for(auto& player : players) + GetEntPool().Delete(player->GetEntity()); +} + +//TODO: shouldn't this be a copy constructor or something? +World* GameInstance::DuplicateWorld() +{ + if(!m_world) + return nullptr; + + World* world = new World(this); + world->m_associatedEntities = m_world->m_associatedEntities; + + return world; +} + +void GameInstance::CreateActors() +{ + auto playerStarts = GetEntPool().GetOf(); + for(auto& playerStart : playerStarts) + { + Log::Print("Spawning local player..."); + + Entity* player = GetEntPool().CreateEntity("Player", m_world); + player->GetTransform()->position = playerStart->GetEntity()->GetTransform()->position; + player->Add(); + player->Add()->SetHeight(3.3f); + + player->Add()->SetRotation(false); + player->Get()->SetMass(5.0f); + player->Get()->SetFriction(0.0f); + player->Get()->EnableDeactivation(true); + + Entity* playerCamera = GetEntPool().CreateEntity("Player camera", m_world); + playerCamera->GetTransform()->SetParent(player->GetTransform()); + playerCamera->GetTransform()->position = glm::vec3(0, 3.3f, 0); + playerCamera->Add()->fieldOfView = 50.0f; + playerCamera->Add(); + } +} + +void GameInstance::SaveOptions() +{ + nlohmann::json file; + + file["fullscreen"] = options.fullscreen; + file["vsync"] = options.vsync; + + std::ofstream out; + out.open("options.json"); + + out << file.dump(1); + out.close(); +} + +void GameInstance::LoadOptions() +{ + std::ifstream in("options.json"); + if(in.is_open()) + { + try + { + nlohmann::json file; + file << in; + + options.fullscreen = file["fullscreen"]; + options.vsync = file["vsync"]; + } + catch(std::exception& exception) + { + Log::Error("Failed to parse options.json! (%s)", exception.what()); + } + } + else + { + Log::Error("Failed to read options.json file! Loading defaults..."); + + options.fullscreen = false; + options.vsync = true; + + SaveOptions(); + } +} \ No newline at end of file diff --git a/engine/core/src/physicssystem.cpp b/engine/core/src/physicssystem.cpp new file mode 100644 index 0000000..c444ea9 --- /dev/null +++ b/engine/core/src/physicssystem.cpp @@ -0,0 +1,190 @@ +#include "physicssystem.hpp" + +#include +#include +#include +#include + +void PhysicsSystem::Initialize() +{ + Log::Print("Initializing physics system..."); + + m_broadPhase = new btDbvtBroadphase(); + + m_collisionConfiguration = new btDefaultCollisionConfiguration(); + m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); + + m_solver = new btSequentialImpulseConstraintSolver; + + m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadPhase, m_solver, m_collisionConfiguration); + m_dynamicsWorld->setGravity(btVector3(0, -9.8f, 0)); +} + +void PhysicsSystem::Cleanup() +{ + delete m_dynamicsWorld; + + delete m_solver; + delete m_dispatcher; + delete m_collisionConfiguration; + delete m_broadPhase; + + Log::Print("Cleaned up physics system"); +} + +void PhysicsSystem::Update() +{ + m_dynamicsWorld->stepSimulation(Time::GetDeltaTime(), 10); + + //collider initializer + { + auto boxColliders = GetEntPool().GetOf(); + for(auto& collider : boxColliders) + { + if(collider->isDirty) + { + collider->collisionShape = new btBoxShape( + btVector3(collider->GetSize().x, collider->GetSize().y, collider->GetSize().z)); + + if(collider->GetEntity()->Get() != nullptr) + collider->GetEntity()->Get()->isDirty = true; + + collider->isDirty = false; + } + } + + auto capsuleColliders = GetEntPool().GetOf(); + for(auto& collider : capsuleColliders) + { + if(collider->isDirty) + { + collider->collisionShape = new btCapsuleShape(0.5f, collider->GetHeight()); + + if(collider->GetEntity()->Get() != nullptr) + collider->GetEntity()->Get()->isDirty = true; + + collider->isDirty = false; + } + } + } + + auto rigidbodies = GetEntPool().GetOf(); + for(auto& rigidbody : rigidbodies) + { + if(rigidbody->isDirty) + { + if(rigidbody->rigidbody != nullptr) + m_dynamicsWorld->removeRigidBody(rigidbody->rigidbody); + + glm::vec3 position = rigidbody->GetEntity()->GetTransform()->position; + glm::quat orientation = rigidbody->GetEntity()->GetTransform()->orientation; + + btDefaultMotionState* motionState = new btDefaultMotionState(btTransform( + btQuaternion(orientation.w, orientation.x, orientation.y, orientation.z), + btVector3(position.x, position.y, position.z))); + + btVector3 fallInerita(0, 0, 0); + + btCompoundShape* compoundShape = new btCompoundShape(); + + auto boxColliders = rigidbody->GetEntity()->GetComponentsOf(); + for(auto boxCollider : boxColliders) + { + glm::vec3 origin = boxCollider->GetEntity()->GetTransform()->position + boxCollider->GetOrigin(); + + btTransform localTrans; + localTrans.setIdentity(); + localTrans.setOrigin(btVector3(origin.x, origin.y, origin.z)); + + compoundShape->addChildShape(localTrans, boxCollider->collisionShape); + } + + auto capsuleColliders = rigidbody->GetEntity()->GetComponentsOf(); + for(auto capsuleCollider : capsuleColliders) + { + btTransform localTrans; + localTrans.setIdentity(); + + compoundShape->addChildShape(localTrans, capsuleCollider->collisionShape); + } + + float mass = rigidbody->GetMass(); + + if(!rigidbody->IsKinematic()) + { + compoundShape->calculateLocalInertia(mass, fallInerita); + } + else + { + mass = 0; + } + + btRigidBody::btRigidBodyConstructionInfo rigidBodyConstructionInfo = btRigidBody::btRigidBodyConstructionInfo( + mass, + motionState, + compoundShape, + fallInerita + ); + rigidbody->rigidbody = new btRigidBody(rigidBodyConstructionInfo); + + if(rigidbody->IsDeactivationEnabled()) + rigidbody->rigidbody->setActivationState(DISABLE_DEACTIVATION); + + if(!rigidbody->IsRotationEnabled()) + rigidbody->rigidbody->setAngularFactor(btVector3(0, 0, 0)); + + rigidbody->rigidbody->setFriction(rigidbody->GetFriction()); + + m_dynamicsWorld->addRigidBody(rigidbody->rigidbody); + + rigidbody->isDirty = false; + } + + m_dynamicsWorld->updateSingleAabb(rigidbody->rigidbody); //update collision shape if needed + + if(rigidbody->GetForce() != glm::vec3(0)) + { + rigidbody->rigidbody->setLinearVelocity(btVector3(rigidbody->GetForce().x, rigidbody->GetForce().y, rigidbody->GetForce().z)); + rigidbody->SetForce(glm::vec3(0)); + } + + if(!rigidbody->IsKinematic()) + { + btTransform trans; + rigidbody->rigidbody->getMotionState()->getWorldTransform(trans); + + btQuaternion rot = trans.getRotation(); + + rigidbody->GetEntity()->GetTransform()->position = glm::vec3(trans.getOrigin().getX(), trans.getOrigin().getY(), trans.getOrigin().getZ()); + + if(rigidbody->IsRotationEnabled()) + rigidbody->GetEntity()->GetTransform()->orientation = glm::quat(rot.getW(), rot.getX(), rot.getY(), rot.getZ()); + } + else + { + btTransform trans; + + glm::vec3 entpos = rigidbody->GetEntity()->GetTransform()->position; + glm::quat entrot = rigidbody->GetEntity()->GetTransform()->orientation; + glm::vec3 entscale = rigidbody->GetEntity()->GetTransform()->scale; + + trans.setOrigin(btVector3(entpos.x, entpos.y, entpos.z)); + trans.setRotation(btQuaternion(entrot.w, entrot.x, entrot.y, entrot.z)); + + rigidbody->rigidbody->getCollisionShape()->setLocalScaling(btVector3(entscale.x, entscale.y, entscale.z)); + + rigidbody->rigidbody->getMotionState()->setWorldTransform(trans); + } + } +} + +bool PhysicsSystem::Raycast(glm::vec3 start, glm::vec3 end) +{ + btVector3 s(start.x, start.y, start.z); + btVector3 e(end.x, end.y, end.z); + + btCollisionWorld::ClosestRayResultCallback RayCallback(s, e); + m_dynamicsWorld->rayTest(s, e, RayCallback); + + return RayCallback.hasHit(); +} \ No newline at end of file diff --git a/engine/core/src/scriptsystem.cpp b/engine/core/src/scriptsystem.cpp new file mode 100644 index 0000000..6c33a43 --- /dev/null +++ b/engine/core/src/scriptsystem.cpp @@ -0,0 +1,186 @@ +#include "scriptsystem.hpp" + +#include +#include +#include +#include +#include +#include + +void MessageCallback(const asSMessageInfo* msg, void*) +{ + std::string type; + switch(msg->type) + { + case asMSGTYPE_ERROR: + type = "ERROR"; + break; + case asMSGTYPE_INFORMATION: + type = "INFO"; + break; + case asMSGTYPE_WARNING: + type = "WARNING"; + break; + default: + break; + } + + Log::Error("%s (%i, %i) %s: %s", msg->section, msg->row, msg->col, type.c_str(), msg->message); +} + +void DebugPrint(const std::string& in) +{ + Log::Print("Script: %s", in.c_str()); +} + +glm::vec3 addvec3(glm::vec3 a, glm::vec3& b) +{ + return a + b; +} + +glm::vec3 mulvec3f(float f, glm::vec3& b) +{ + return b * f; +} + +float GetRandomf() +{ + srand(static_cast (time(0))); + return static_cast (rand()) / static_cast (1.0f); +} + +float mathSin(float num) +{ + return glm::sin(num); +} + +Transform* GetEntTransform(Entity* entity) +{ + return entity->GetTransform(); +} + +float utilityGetTime() +{ + return Platform::GetTime(); +} + +/* + * TODO: This is a ugly hack to get the current entity + * It may change, but the function will not, so don't worry about your scripts breaking. + */ +Entity* ScriptSystem::GetEntity() +{ + //get from userdata 2001 (what name is the current module) + asIScriptModule* module = m_scriptEngine->GetModule(static_cast(m_scriptEngine->GetUserData(2001))); + + return static_cast(module->GetUserData(2000)); +} + +void ScriptSystem::Initialize() +{ + Log::Print("Initializing script system..."); + + m_scriptEngine = asCreateScriptEngine(); + RegisterStdString(m_scriptEngine); + + m_scriptEngine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); + + m_scriptEngine->RegisterGlobalFunction("void Print(const string &in)", asFUNCTION(DebugPrint), asCALL_CDECL); + m_scriptEngine->RegisterGlobalFunction("float sin(float num)", asFUNCTION(mathSin), asCALL_CDECL); + m_scriptEngine->RegisterGlobalFunction("float GetRandomf()", asFUNCTION(GetRandomf), asCALL_CDECL); + m_scriptEngine->RegisterGlobalFunction("float GetTimef()", asFUNCTION(utilityGetTime), asCALL_CDECL); + + m_scriptEngine->RegisterObjectType("Vector3", sizeof(glm::vec3), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); + + m_scriptEngine->RegisterObjectProperty("Vector3", "float x", asOFFSET(glm::vec3, x)); + m_scriptEngine->RegisterObjectProperty("Vector3", "float y", asOFFSET(glm::vec3, y)); + m_scriptEngine->RegisterObjectProperty("Vector3", "float z", asOFFSET(glm::vec3, z)); + + m_scriptEngine->RegisterObjectMethod("Vector3", "Vector3 opAdd(Vector3) const", asFUNCTION(addvec3), asCALL_CDECL_OBJLAST); + m_scriptEngine->RegisterObjectMethod("Vector3", "Vector3 opMul(float) const", asFUNCTION(mulvec3f), asCALL_CDECL_OBJLAST); + + //TODO: add quaternions and other misc types + m_scriptEngine->RegisterObjectType("Transform", 0, asOBJ_REF | asOBJ_NOCOUNT); + m_scriptEngine->RegisterObjectProperty("Transform", "Vector3 position", asOFFSET(Transform, position)); + m_scriptEngine->RegisterObjectProperty("Transform", "Vector3 scale", asOFFSET(Transform, scale)); + + m_scriptEngine->RegisterObjectType("Entity", 0, asOBJ_REF | asOBJ_NOCOUNT); + m_scriptEngine->RegisterObjectProperty("Entity", "string name", asOFFSET(Entity, name)); + + m_scriptEngine->RegisterObjectMethod("Entity", "Transform& GetTransform()", asFUNCTION(GetEntTransform), asCALL_CDECL_OBJLAST); + + //helper functions + m_scriptEngine->RegisterGlobalFunction("Entity& GetEntity()", asMETHOD(ScriptSystem, GetEntity), asCALL_THISCALL_ASGLOBAL, this); +} + +void ScriptSystem::Cleanup() +{ + m_scriptEngine->ShutDownAndRelease(); + + Log::Print("Cleaned up script system"); +} + +void ScriptSystem::RunFunction(const std::string& name, const std::string& module_name) +{ + asIScriptModule* module = m_scriptEngine->GetModule(module_name.c_str()); + asIScriptFunction* function = module->GetFunctionByDecl(name.c_str()); + + //check if the function is defined + if(function != nullptr) + { + asIScriptContext* context = m_scriptEngine->CreateContext(); + context->Prepare(function); + + int result = context->Execute(); + if(result != asEXECUTION_FINISHED && result == asEXECUTION_EXCEPTION) + Log::Error("Script exception %s occured in module %s function %s", context->GetExceptionString(), module_name.c_str(), name.c_str()); + + context->Release(); + } +} + +void ScriptSystem::Update() +{ + auto scripts = GetEntPool().GetOf(); + for(auto& actor : scripts) + { + if(!actor->intialized) + { + std::string path = "game/" + actor->scriptPath; + + Log::Print("Loading script %s", path.c_str()); + + asIScriptModule* mod = m_scriptEngine->GetModule(actor->scriptPath.c_str(), asGM_ALWAYS_CREATE); + + std::ifstream in(path); + if(in.is_open()) + { + std::string script; + script.assign(std::istreambuf_iterator(in), std::istreambuf_iterator()); + + mod->AddScriptSection(actor->scriptPath.c_str(), script.c_str()); + mod->Build(); + + mod->SetUserData(actor->GetEntity(), 2000); + m_scriptEngine->SetUserData(&actor->scriptPath[0], 2001); + + RunFunction("void Start()", actor->scriptPath); + } + else + { + Log::Error("Cannot find script %s!", path.c_str()); + } + + actor->intialized = true; + } + else + { + asIScriptModule* mod = m_scriptEngine->GetModule(actor->scriptPath.c_str()); + + mod->SetUserData(actor->GetEntity(), 2000); + m_scriptEngine->SetUserData(&actor->scriptPath[0], 2001); + + RunFunction("void Update()", actor->scriptPath); + } + } +} diff --git a/engine/core/src/world.cpp b/engine/core/src/world.cpp new file mode 100644 index 0000000..fc4d582 --- /dev/null +++ b/engine/core/src/world.cpp @@ -0,0 +1,111 @@ +#include "world.hpp" + +#include +#include +#include +#include + +World::~World() +{ + for(auto id : m_associatedEntities) + GetEntPool().Delete(id); + + std::vector().swap(m_associatedEntities); + + DrawList::InvalidateNextFrame(); +} + +void World::LoadFromMap(Map* map) +{ + m_path = map->GetPath(); + + nlohmann::json file = map->GetContents(); + nlohmann::json entities = file["entities"]; + for(auto entry : entities) + { + std::string name = entry["name"]; + std::string tag = entry["tag"]; + + Entity* entity = GetEntPool().CreateEntity(name, this, entry["id"]); + entity->tag = tag; + + nlohmann::json components = entry["components"]; + for(auto centry : components) + { + IntermediateData data = centry; + + TypeTracker::RebuildTypes(); + + if(centry["class"] == "Transform") + { + entity->GetTransform()->Load(data); + } + else + { + Component* component = TypeTracker::ComponentFromString(centry["class"]); + if(component != nullptr) + { + entity->Add(component)->Load(data); + + AssetManager::LoadAssets(component->RequiredAssets()); + } + } + } + } + + skyboxID = file["skybox_id"]; + AssetManager::LoadAssets({ skyboxID }); +} + +void World::AddEntity(Entity* entity) +{ + m_associatedEntities.push_back(entity->GetID()); +} + +void World::AddEntity(int id) +{ + m_associatedEntities.push_back(id); +} + +nlohmann::json World::DumpToJSON() +{ + nlohmann::json file; + nlohmann::json& ents = file["entities"]; + + auto entities = GetEntPool().GetAllEntities(); + auto components = GetEntPool().GetAllComponents(); + for(auto entity : entities) + { + if(entity->tag == "editor") + continue; + + nlohmann::json ent; + ent["name"] = entity->name; + ent["id"] = entity->GetID(); + ent["tag"] = entity->tag; + + nlohmann::json& comps = ent["components"]; + + for(auto& itr : components) + { + std::string n = Utility::GetTypeName(itr.first.type); + + if(itr.first.entity == entity) + { + IntermediateData data; + itr.second->Save(data); + + nlohmann::json comp = data.GetJSON(); + + comp["class"] = n; + comps.push_back(comp); + } + } + + ents.push_back(ent); + } + + file["skybox_id"] = skyboxID; + + return file; +} \ No newline at end of file diff --git a/engine/ecs/CMakeLists.txt b/engine/ecs/CMakeLists.txt new file mode 100644 index 0000000..1c73655 --- /dev/null +++ b/engine/ecs/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SOURCE_FILES + src/entity.cpp + src/component.cpp + src/entitypool.cpp + src/typetracker.cpp) + +add_library(ECS ${SOURCE_FILES}) + +include_directories(include) +include_directories(${UTILITY_INCLUDE_DIR} ${CORE_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR}) +include_directories(SYSTEM ${LIBRARY_JSON_INCLUDE_DIR} ${LIBRARY_GLM_INCLUDE_DIR} ${LIBRARY_VULKAN_INCLUDE_DIR}) \ No newline at end of file diff --git a/engine/ecs/include/component.hpp b/engine/ecs/include/component.hpp new file mode 100644 index 0000000..a70c0ba --- /dev/null +++ b/engine/ecs/include/component.hpp @@ -0,0 +1,487 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +struct DebugResult +{ + struct DebugCube + { + DebugCube(glm::vec3 o, glm::vec3 s) : origin(o), size(s) {} + + glm::vec3 origin, size; + }; + + void AddCube(glm::vec3 origin, glm::vec3 size) + { + debugCubes.push_back(DebugCube(origin, size)); + } + + std::vector debugCubes; +}; + +class Entity; + +class Component +{ +friend class EntityPool; + +public: + virtual void Load(IntermediateData&) {} + virtual void Save(IntermediateData&) {} + + virtual DebugResult DrawDebug() { return DebugResult(); } + + virtual std::vector RequiredAssets() + { + return std::vector(); + } + + Entity* GetEntity() + { + return entity_handle; + } + +protected: + void SetEntity(Entity* entity) + { + entity_handle = entity; + } + +private: + Entity* entity_handle = nullptr; +}; + +class Transform : public Component +{ +public: + void Load(IntermediateData& data) override + { + position = data["position"]; + orientation = data["orientation"]; + scale = data["scale"]; + + parentID = data["parent"]; + } + + void Save(IntermediateData& data) override + { + data["position"] = position; + data["orientation"] = orientation; + data["scale"] = scale; + + data["parent"] = parentID; + } + + Transform* GetParent(); + void SetParent(Transform* transform); + + void SetEulerAngles(const glm::vec3& rot) + { + orientation = glm::quat(glm::vec3(glm::radians(rot.x), glm::radians(rot.y), glm::radians(rot.z))); + } + + glm::vec3 GetEulerAngles() + { + glm::vec3 euler = glm::eulerAngles(orientation); + return glm::vec3(glm::degrees(euler.x), glm::degrees(euler.y), glm::degrees(euler.z)); + } + + glm::quat GetWorldOrientation() + { + glm::quat first = orientation; + glm::quat second; + + if(GetParent() != nullptr) + second = GetParent()->orientation; + + return second * first; + } + + glm::vec3 GetWorldPosition() + { + return GetModel() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + } + + glm::vec3 GetForward() + { + return GetWorldOrientation() * glm::vec3(0.0f, 0.0f, 1.0f); + } + + glm::vec3 GetUp() + { + return GetWorldOrientation() * glm::vec3(0.0f, 1.0f, 0.0f); + } + + glm::vec3 GetRight() + { + return GetWorldOrientation() * glm::vec3(1.0f, 0.0f, 0.0f); + } + + glm::mat4 GetModel() + { + glm::mat4 tmp(1.0f); + + Transform* parent = GetParent(); + if(parent != nullptr) + { + tmp = glm::translate(tmp, parent->position); + tmp *= glm::toMat4(parent->orientation); + tmp = glm::scale(tmp, parent->scale); + } + + tmp = glm::translate(tmp, position); + tmp *= glm::toMat4(orientation); + tmp = glm::scale(tmp, scale); + + return tmp; + } + + std::vector GetChildren(); + + glm::vec3 position = glm::vec3(0); + glm::quat orientation = glm::quat(); + glm::vec3 scale = glm::vec3(1); + + int parentID = 0; +}; + +class MeshRenderer : public Component +{ +public: + void Load(IntermediateData& data) override + { + meshID = data["meshid"]; + materialID = data["matid"]; + isStaticGeometry = data["static"]; + } + + void Save(IntermediateData& data) override + { + data["meshid"] = meshID; + data["matid"] = materialID; + data["static"] = isStaticGeometry; + } + + std::vector RequiredAssets() override + { + return { meshID, materialID }; + } + + int meshID = 0; + int materialID = 0; + bool isStaticGeometry = false; +}; + +class Light : public Component +{ +public: + void Load(IntermediateData& data) override + { + color = data["color"]; + radius = data["radius"]; + type = data["type"]; + shadowsEnabled = data["shadows"]; + shadowFrustumSize = data["shadowsize"]; + shadowZFar = data["shadowfar"]; + } + + void Save(IntermediateData& data) override + { + data["color"] = color; + data["radius"] = radius; + data["type"] = type; + data["shadows"] = shadowsEnabled; + data["shadowsize"] = shadowFrustumSize; + data["shadowfar"] = shadowZFar; + } + + glm::mat4 GetProjection() + { + return glm::ortho(-shadowFrustumSize, shadowFrustumSize, -shadowFrustumSize, shadowFrustumSize, 0.1f, shadowZFar); + } + + glm::vec3 color = glm::vec3(1); + float radius = 15.0f; + + enum Type + { + Directional = 0, + Point = 1 + }; + + int type = Point; + bool shadowsEnabled = false; + float shadowFrustumSize = 25.0f; + float shadowZFar = 50.0f; +}; + +class Camera : public Component +{ +public: + void Load(IntermediateData& data) override + { + fieldOfView = static_cast(data["fov"]); + } + + void Save(IntermediateData& data) override + { + data["fov"] = static_cast(fieldOfView); + } + + glm::mat4 GetProjection(); + glm::mat4 GetView(); + + float fieldOfView = 45.0f; +}; + +class ScriptActor : public Component +{ +public: + void Load(IntermediateData& data) override + { + scriptPath = std::string(data["scriptpath"]); + } + + void Save(IntermediateData& data) override + { + data["scriptpath"] = scriptPath; + } + + std::string scriptPath; + bool intialized = false; +}; + +class btRigidBody; +class btCollisionShape; + +class Rigidbody : public Component +{ + friend class RigidbodyInspector; + +public: + void Load(IntermediateData& data) override + { + m_mass = data["mass"]; + m_friction = data["friction"]; + + m_enableRotation = data["enablerotation"]; + m_kinematic = data["kinematic"]; + m_enableDeactivation = data["enabledeactivation"]; + } + + void Save(IntermediateData& data) override + { + data["mass"] = m_mass; + data["friction"] = m_friction; + + data["enablerotation"] = m_enableRotation; + data["kinematic"] = m_kinematic; + data["enabledeactivation"] = m_enableDeactivation; + } + + void SetMass(float m) + { + m_mass = m; + isDirty = true; + } + + float GetMass() + { + return m_mass; + } + + void SetFriction(float f) + { + m_friction = f; + isDirty = true; + } + + float GetFriction() { + return m_friction; + } + + void EnableKinematic(bool k) + { + m_kinematic = k; + isDirty = true; + } + + bool IsKinematic() + { + return m_kinematic; + } + + void EnableDeactivation(bool d) + { + m_enableDeactivation = d; + isDirty = true; + } + + bool IsDeactivationEnabled() + { + return m_enableDeactivation; + } + + void SetRotation(bool r) + { + m_enableRotation = r; + isDirty = true; + } + + bool IsRotationEnabled() + { + return m_enableRotation; + } + + void AddForce(glm::vec3 dir) + { + m_force += dir; + } + + void SetForce(glm::vec3 val) + { + m_force = val; + } + + glm::vec3 GetForce() + { + return m_force; + } + + bool isDirty = true; + + btRigidBody* rigidbody = nullptr; + +private: + bool m_kinematic = false; + bool m_enableDeactivation = true; + bool m_enableRotation = true; + + float m_mass = 10; + float m_friction = 1.0f; + + glm::vec3 m_force; +}; + +class Collider : public Component +{ +public: + btCollisionShape* collisionShape; + bool isDirty = true; +}; + +class BoxCollider : public Collider +{ + friend class BoxColliderInspector; + +public: + void Load(IntermediateData& data) override + { + m_size = data["boxsize"]; + m_origin = data["boxorigin"]; + } + + void Save(IntermediateData& data) override + { + data["boxsize"] = m_size; + data["boxorigin"] = m_origin; + } + + DebugResult DrawDebug() override + { + DebugResult result; + result.AddCube(m_origin, m_size); + + return result; + } + + void SetOrigin(glm::vec3 origin) + { + m_origin = origin; + isDirty = true; + } + + glm::vec3 GetOrigin() + { + return m_origin; + } + + void SetSize(glm::vec3 size) + { + m_size = size; + isDirty = true; + } + + glm::vec3 GetSize() + { + return m_size; + } + +private: + glm::vec3 m_size = glm::vec3(1); + glm::vec3 m_origin = glm::vec3(0); +}; + +class CapsuleCollider : public Collider +{ +public: + void Load(IntermediateData& data) override + { + m_height = data["height"]; + } + + void Save(IntermediateData& data) override + { + data["height"] = m_height; + } + + void SetHeight(float val) + { + m_height = val; + isDirty = true; + } + + float GetHeight() + { + return m_height; + } + +private: + float m_height = 1.0f; +}; + +class PlayerStart : public Component +{ +public: + +}; + +class Player : public Component +{ +public: + bool initialized = false; + glm::vec3 previousDirection; +}; + +class PlayerCamera : public Component +{ +public: + float yaw = 0, pitch = 0; + float lastxoffset = 0, lastyoffset = 0; + float lastYaw = 0, lastPitch = 0; +}; + +/* + * Used for dynamic environment mapping (objects that are in the scene) + */ +class EnvironmentProbe : public Component +{ +public: + +}; \ No newline at end of file diff --git a/engine/ecs/include/entity.hpp b/engine/ecs/include/entity.hpp new file mode 100644 index 0000000..4ee4513 --- /dev/null +++ b/engine/ecs/include/entity.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "entitypool.hpp" +#include "typetracker.hpp" +#include "component.hpp" + +class GameInstance; +class World; + +class Entity +{ + friend class EntityPool; + +public: + uint32_t GetID() + { + return m_id; + } + + template + T* Add() + { + T* t = new T; + GetEntPool().RegisterComponent(this, t); + + return t; + } + + Component* Add(Component* component) + { + GetEntPool().RegisterComponent(this, component); + + return component; + } + + Component* Add(const std::string& componentName) + { + auto types = TypeTracker::GetTypes(); + for(auto type : types) + { + if(type.first == componentName) + return Add(type.second); + } + + return nullptr; + } + + Component* Add(std::type_index type) + { + return Add(Utility::GetTypeName(type)); + } + + template + void Remove() + { + GetEntPool().RemoveComponent(this, typeid(T)); + } + + void Remove(Component* component) + { + GetEntPool().RemoveComponent(this, component); + } + + void RemoveAll() + { + GetEntPool().RemoveComponents(this); + } + + void Duplicate(Component* component) + { + GetEntPool().DuplicateComponent(this, component); + } + + template + T* Get() + { + auto components = GetEntPool().GetComponents(this); + for(auto& itr : components) + { + if(itr.first.type == typeid(T)) + return static_cast(itr.second); + } + + return nullptr; + } + + Transform* GetTransform() + { + return Get(); + } + + template + std::vector GetComponentsOf() + { + std::vector tmp; + + auto components = GetEntPool().GetComponents(this); + for(auto& itr : components) + { + if(itr.first.type == typeid(T)) + tmp.push_back(static_cast(itr.second)); + } + + return tmp; + } + + std::vector GetComponents() + { + std::vector tmp; + + auto components = GetEntPool().GetComponents(this); + for(auto& itr : components) + { + tmp.push_back(itr.second); + } + + return tmp; + } + + GameInstance* GetGame() const + { + return m_gameInstance; + } + + World* GetWorld() const + { + return m_world; + } + + std::string name; + std::string tag; + +protected: + //you should be using the entity pool! + Entity(const std::string& requestedName = "Entity", uint32_t requestedID = -1); + + GameInstance* m_gameInstance = nullptr; + World* m_world = nullptr; + +private: + uint32_t m_id = -1; +}; diff --git a/engine/ecs/include/entitypool.hpp b/engine/ecs/include/entitypool.hpp new file mode 100644 index 0000000..867b94b --- /dev/null +++ b/engine/ecs/include/entitypool.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Entity; +class Component; +class World; + +struct ComponentInfo +{ + std::type_index type = typeid(nullptr); + Entity* entity = nullptr; + + ComponentInfo(const std::type_index& ti, Entity* ent) + { + type = ti; + entity = ent; + } + + friend bool operator==(const ComponentInfo& a, const ComponentInfo& b) + { + if(a.entity == b.entity && a.type == b.type) + return true; + + return false; + } + + friend bool operator<(const ComponentInfo&, const ComponentInfo&) + { + return false; + } +}; + +namespace std +{ + template<> + struct hash + { + size_t operator()(ComponentInfo const& x) const noexcept + { + return (sizeof(x.entity) + sizeof(x.type)); + } + }; +} + +/* + * This is where the entities are actually created/stored. This a replacement for the static functions in previous + * iterations of the entity class + * TODO: recycle our entity pointers + * TODO: don't make this a singleton (it can be set up as a regular object) + */ +class EntityPool +{ + friend class Entity; + +public: + Entity* CreateEntity(World* world, int id = -1); + Entity* CreateEntity(const std::string& name, World* world, int id = -1); //create an entity and attach it to a world + + Entity* GetByID(int id); + + std::multimap GetComponents(Entity* entity); + + template + std::vector GetOf() + { + std::vector tmp; + for(auto& itr : m_components) + { + if(itr.first.type == typeid(T)) + tmp.push_back(static_cast(itr.second)); + } + + return tmp; + } + + std::type_index GetType(Component* component); + + std::vector GetAllEntities() + { + return m_entities; + } + + std::multimap GetAllComponents() + { + return m_components; + } + + Entity* Duplicate(Entity* entity, World* world); + + Entity* Duplicate(int id, World* world) + { + return Duplicate(GetByID(id), world); + } + + void Delete(Entity* entity); + + void Delete(int id) + { + m_deletionQueue.push_back(id); + } + + void ProcessDeletions(); + + void Cleanup(); + + EventDispatcher onEntityListChange; //adding/deleting entities + EventDispatcher onEntityChange; //components being added/removed + +protected: + //for internal use + void RegisterComponent(Entity* entity, Component* component); + + void RemoveComponent(Entity* entity, std::type_index type); + void RemoveComponent(Entity* entity, Component* component); + void RemoveComponents(Entity* entity); + + void DuplicateComponent(Entity* entity, Component* component); + +private: + void DeleteInternal(Entity* entity); + + std::vector m_entities; + std::multimap m_components; + + static std::vector m_deletionQueue; +}; + +inline EntityPool& GetEntPool() +{ + static EntityPool pool; + + return pool; +} \ No newline at end of file diff --git a/engine/ecs/include/typetracker.hpp b/engine/ecs/include/typetracker.hpp new file mode 100644 index 0000000..9b97869 --- /dev/null +++ b/engine/ecs/include/typetracker.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +class Component; + +class TypeTracker +{ +public: + static void RebuildTypes(); + + template + static void RegisterType() + { + m_types.emplace(Utility::GetTypeName(typeid(T)), new T); + } + + static Component* ComponentFromString(const std::string& name) + { + for(auto& type : m_types) + { + if (type.first == name) + return type.second; + } + + return nullptr; + } + + static std::vector GetTypeNames() + { + std::vector tmp; + for(auto type : m_types) + tmp.push_back(type.first); + + return tmp; + } + static std::map GetTypes() + { + return m_types; + } + +private: + static std::map m_types; +}; \ No newline at end of file diff --git a/engine/ecs/src/component.cpp b/engine/ecs/src/component.cpp new file mode 100644 index 0000000..1c4908e --- /dev/null +++ b/engine/ecs/src/component.cpp @@ -0,0 +1,52 @@ +#include "component.hpp" + +#include +#include +#include + +Transform* Transform::GetParent() +{ + Entity* parentEntity = GetEntPool().GetByID(parentID); + if(parentEntity != nullptr) + { + return parentEntity->GetTransform(); + } + + return nullptr; +} + +void Transform::SetParent(Transform *transform) +{ + if(transform == nullptr) + { + parentID = 0; + } + else + { + parentID = transform->GetEntity()->GetID(); + } +} + +std::vector Transform::GetChildren() +{ + auto transforms = GetEntPool().GetOf(); + std::vector tmp; + + for(unsigned int i = 0; i < transforms.size(); i++) + { + if(transforms[i]->parentID == GetEntity()->GetID() && transforms[i] != this) + tmp.push_back(transforms[i]); + } + + return tmp; +} + +glm::mat4 Camera::GetProjection() +{ + return glm::perspective(glm::radians(fieldOfView), GFX::GetScreenAspectRatio(), 0.1f, 1000.0f); +} + +glm::mat4 Camera::GetView() +{ + return glm::inverse(GetEntity()->GetTransform()->GetModel()); +} \ No newline at end of file diff --git a/engine/ecs/src/entity.cpp b/engine/ecs/src/entity.cpp new file mode 100644 index 0000000..3b71659 --- /dev/null +++ b/engine/ecs/src/entity.cpp @@ -0,0 +1,17 @@ +#include "entity.hpp" + +Entity::Entity(const std::string& requestedName, uint32_t requestedID) +{ + name = requestedName; + + Add(); + + if(requestedID != static_cast(-1)) + { + m_id = requestedID; + } + else + { + m_id = Utility::Random(1, 10000); + } +} \ No newline at end of file diff --git a/engine/ecs/src/entitypool.cpp b/engine/ecs/src/entitypool.cpp new file mode 100644 index 0000000..44a3ce5 --- /dev/null +++ b/engine/ecs/src/entitypool.cpp @@ -0,0 +1,225 @@ +#include "entitypool.hpp" + +#include +#include "entity.hpp" + +std::vector EntityPool::m_deletionQueue; + +Entity* EntityPool::CreateEntity(World* world, int id) +{ + return CreateEntity("Entity", world, id); +} + +Entity* EntityPool::CreateEntity(const std::string& name, World* world, int id) +{ + Entity* entity = new Entity(name, id); + + if(world != nullptr) + world->AddEntity(entity); + + m_entities.push_back(entity); + + onEntityListChange(); + + return entity; +} + +Entity* EntityPool::GetByID(int id) +{ + for(auto entity : m_entities) + { + if(entity->GetID() == id) + return entity; + } + + return nullptr; +} + +void EntityPool::RegisterComponent(Entity* entity, Component* component) +{ + component->SetEntity(entity); + m_components.insert(std::pair(ComponentInfo(typeid(*component), entity), component)); + + onEntityChange(); +} + +void EntityPool::RemoveComponent(Entity* entity, Component* component) +{ + auto itr = m_components.begin(); + while(itr != m_components.end()) + { + if((*itr).first.entity == entity && (*itr).second == component) + { + itr = m_components.erase(itr); + + onEntityChange(); + + break; + } + else + { + itr++; + } + } +} + +void EntityPool::RemoveComponent(Entity* entity, std::type_index type) +{ + auto itr = m_components.begin(); + while(itr != m_components.end()) + { + if((*itr).first.entity == entity && (*itr).first.type == type) + { + itr = m_components.erase(itr); + + onEntityChange(); + + break; + } + else + { + itr++; + } + } +} + +void EntityPool::RemoveComponents(Entity* entity) +{ + auto itr = m_components.begin(); + while(itr != m_components.end()) + { + if((*itr).first.entity == entity) + { + itr = m_components.erase(itr); + + onEntityChange(); + } + else + { + itr++; + } + } +} + +std::multimap EntityPool::GetComponents(Entity* entity) +{ + std::multimap tmp; + for(auto& itr : m_components) + { + if(itr.first.entity == entity) + tmp.emplace(itr.first, itr.second); + } + + return tmp; +} + +std::type_index EntityPool::GetType(Component* component) +{ + for(auto& itr : m_components) + { + if(itr.second == component) + return itr.first.type; + } + + return typeid(Component); +} + +void EntityPool::ProcessDeletions() +{ + while(!m_deletionQueue.empty()) + { + DeleteInternal(GetByID(m_deletionQueue.back())); + m_deletionQueue.pop_back(); + } + + m_deletionQueue.clear(); +} + +Entity* EntityPool::Duplicate(Entity* entity, World* world) +{ + Entity* newEntity = CreateEntity(entity->name, world); + + IntermediateData transformData; + entity->GetTransform()->Save(transformData); + newEntity->GetTransform()->Load(transformData); + + TypeTracker::RebuildTypes(); + + auto itr = m_components.begin(); + while(itr != m_components.end()) + { + if((*itr).first.entity == entity) + { + if((*itr).first.type != typeid(Transform)) + { + IntermediateData data; + (*itr).second->Save(data); + + newEntity->Add(Utility::GetTypeName((*itr).first.type))->Load(data); + } + } + + itr++; + } + + auto children = entity->GetTransform()->GetChildren(); + for(auto child : children) + { + Entity* newChild = Duplicate(child->GetEntity(), world); + newChild->GetTransform()->SetParent(newEntity->GetTransform()); + } + + return newEntity; +} + +void EntityPool::Delete(Entity* entity) +{ + m_deletionQueue.push_back(entity->GetID()); +} + +void EntityPool::DuplicateComponent(Entity* entity, Component* component) +{ + TypeTracker::RebuildTypes(); + + IntermediateData data; + component->Save(data); + + entity->Add(GetType(component))->Load(data); +} + +void EntityPool::DeleteInternal(Entity* entity) +{ + auto children = entity->GetTransform()->GetChildren(); + for(auto child : children) + { + DeleteInternal(child->entity_handle); + } + + entity->RemoveAll(); + + for(auto it = m_entities.begin(); it != m_entities.end();) + { + if(*it == entity) + { + it = m_entities.erase(it); + } + else + { + ++it; + } + } + + onEntityListChange(); +} + +void EntityPool::Cleanup() +{ + std::vector().swap(m_deletionQueue); + + std::vector().swap(m_entities); //makes sure the vector gets deallocated + + std::multimap().swap(m_components); + + //std::function().swap(onEntityListChange); + //std::function().swap(onEntityChange); +} \ No newline at end of file diff --git a/engine/ecs/src/typetracker.cpp b/engine/ecs/src/typetracker.cpp new file mode 100644 index 0000000..6fce7a6 --- /dev/null +++ b/engine/ecs/src/typetracker.cpp @@ -0,0 +1,25 @@ +#include "typetracker.hpp" + +#include "component.hpp" + +std::map TypeTracker::m_types; + +void TypeTracker::RebuildTypes() +{ + m_types.clear(); + + /* + * Only register types here that you want saved! + * TODO: add option to disable serialization + */ + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); + RegisterType(); +} \ No newline at end of file diff --git a/engine/input/CMakeLists.txt b/engine/input/CMakeLists.txt new file mode 100644 index 0000000..e3b9ae5 --- /dev/null +++ b/engine/input/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Input src/input.cpp) + +include_directories(include) \ No newline at end of file diff --git a/engine/input/include/input.hpp b/engine/input/include/input.hpp new file mode 100644 index 0000000..71ba2ec --- /dev/null +++ b/engine/input/include/input.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include + +enum MouseButton +{ + Left = 0, + Right = 1 +}; + +enum Key +{ + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, + F = 5, + G = 6, + H = 7, + I = 8, + J = 9, + K = 10, + L = 11, + M = 12, + N = 13, + O = 14, + P = 15, + Q = 16, + R = 17, + S = 18, + T = 19, + U = 20, + V = 21, + W = 22, + X = 23, + Y = 24, + Z = 25, + Tab = 26, + LeftArrow = 27, + RightArrow = 28, + UpArrow = 29, + DownArrow = 30, + PageUp = 31, + PageDown = 32, + Home = 33, + End = 34, + Delete = 35, + Backspace = 36, + Enter = 37, + Escape = 38, + LeftControl = 39, + RightControl = 40, + LeftShift = 41, + RightShift = 42, + LeftAlt = 43, + RightAlt = 44, + LeftSuper = 45, + RightSuper = 46 +}; + +enum KeyState +{ + Release = 0, + Press = 1, + Repeat = 2 +}; + +class Input +{ +public: + static void NewFrame(); //needed for some input like scrolling + + static bool IsMouseDown(int button); + static bool IsMouseClicked(int button); + + static void GetMousePosition(double& x, double& y); + + static float GetScrollDelta(); + + static bool IsKeyDown(int key); + static bool IsKeyPressed(int key); + + static void FeedMouseState(int button, int state); + static void FeedMousePosition(float x, float y); + static void FeedKeyState(int key, int state); + static void FeedScroll(double y); + static void FeedChar(unsigned int c); + + static std::function mouseButtonCallback; + static std::function keyButtonCallback; + static std::function charCallback; + +private: + static int m_mouseState[2]; + static int m_lastMouseState[2]; + + static double m_mouseX; + static double m_mouseY; + + static double m_currentScroll; + + static int m_keyState[26]; +}; \ No newline at end of file diff --git a/engine/input/src/input.cpp b/engine/input/src/input.cpp new file mode 100644 index 0000000..7c25c7d --- /dev/null +++ b/engine/input/src/input.cpp @@ -0,0 +1,84 @@ +#include "input.hpp" + +#include + +int Input::m_mouseState[2]; +int Input::m_lastMouseState[2]; +int Input::m_keyState[26]; + +double Input::m_mouseX, Input::m_mouseY; +double Input::m_currentScroll = 0; + +std::function Input::mouseButtonCallback; +std::function Input::keyButtonCallback; +std::function Input::charCallback; + +void Input::NewFrame() +{ + m_currentScroll = 0; + memcpy(m_lastMouseState, m_mouseState, sizeof(m_mouseState)); +} + +bool Input::IsMouseDown(int button) +{ + return m_mouseState[button] == 1; +} + +bool Input::IsMouseClicked(int button) +{ + return m_lastMouseState[button] == 0 && m_mouseState[button] == 1; +} + +bool Input::IsKeyDown(int key) +{ + return m_keyState[key] == 1 || m_keyState[key] == 2; +} + +bool Input::IsKeyPressed(int key) +{ + return m_keyState[key] == 1; +} + +void Input::GetMousePosition(double& x, double& y) +{ + x = m_mouseX; + y = m_mouseY; +} + +float Input::GetScrollDelta() +{ + return m_currentScroll; +} + +void Input::FeedMouseState(int button, int state) +{ + m_mouseState[button] = state; + + if(mouseButtonCallback) + mouseButtonCallback(button, state); +} + +void Input::FeedMousePosition(float x, float y) +{ + m_mouseX = x; + m_mouseY = y; +} + +void Input::FeedKeyState(int key, int state) +{ + m_keyState[key] = state; + + if(keyButtonCallback) + keyButtonCallback(key, state); +} + +void Input::FeedScroll(double y) +{ + m_currentScroll += y; +} + +void Input::FeedChar(unsigned int c) +{ + if(charCallback) + charCallback(c); +} \ No newline at end of file diff --git a/engine/platform/CMakeLists.txt b/engine/platform/CMakeLists.txt new file mode 100644 index 0000000..8173da9 --- /dev/null +++ b/engine/platform/CMakeLists.txt @@ -0,0 +1,40 @@ +if(EMSCRIPTEN) + set(SOURCE_FILES + ${SOURCE_FILES} + emscripten/filesystem.cpp + emscripten/gfx.cpp + emscripten/inputpump.cpp + emscripten/platform.cpp) +elseif(UNIX) + set(SOURCE_FILES + ${SOURCE_FILES} + unix/filesystem.cpp) +elseif(WIN32) + set(SOURCE_FILES + ${SOURCE_FILES} + windows/filesystem.cpp) +endif() + +if(NOT EMSCRIPTEN) + set(SOURCE_FILES + ${SOURCE_FILES} + glfw/gfx.cpp + glfw/inputpump.cpp + glfw/platform.cpp) +endif() + +add_library(Platform ${SOURCE_FILES}) + +target_link_libraries(Platform Input physfs) + +if(NOT EMSCRIPTEN) + target_link_libraries(Platform glfw) +endif() + +include_directories(common) +include_directories(${UTILITY_INCLUDE_DIR} ${INPUT_INCLUDE_DIR}) +include_directories(SYSTEM + ${LIBRARY_VULKAN_INCLUDE_DIR} + ${LIBRARY_PHYSFS_INCLUDE_DIR} + ${LIBRARY_GLFW_INCLUDE_DIRS} + ${LIBRARY_GLM_INCLUDE_DIR}) \ No newline at end of file diff --git a/engine/platform/common/filesystem.hpp b/engine/platform/common/filesystem.hpp new file mode 100644 index 0000000..5473ab1 --- /dev/null +++ b/engine/platform/common/filesystem.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include + +//used only for appearances atm +//TODO: add actual File struct +typedef char* File; + +class Filesystem +{ +public: + static bool& IsPacked() + { + static bool isPacked = true; + + return isPacked; + } + + static void Init(const char* cwd, std::vector searchPaths) + { + //TODO: allow explicit disable of physfs + if(IsPacked()) + { + PHYSFS_init(cwd); + PHYSFS_permitSymbolicLinks(false); //for security + + for(auto path : searchPaths) + { + PHYSFS_addToSearchPath(("game/" + path).c_str(), 1); + } + + PHYSFS_addToSearchPath("game/data.vpf", 1); + PHYSFS_addToSearchPath("game/", 1); + } + } + + static File GetFileHandle(const std::string& path, int& size, bool forcePacked = false) + { + if(IsPacked() || forcePacked) + { + PHYSFS_file* indexFile = PHYSFS_openRead(path.c_str()); + if(!indexFile) + { + size = 0; + return nullptr; + } + + char* myBuf; + myBuf = new char[PHYSFS_fileLength(indexFile) + 1]; + PHYSFS_read(indexFile, myBuf, 1, PHYSFS_fileLength(indexFile)); + myBuf[PHYSFS_fileLength(indexFile)] = '\0'; + + size = PHYSFS_fileLength(indexFile); + + PHYSFS_close(indexFile); + + return myBuf; + } + else + { + char* buffer; + int string_size, read_size; + FILE* handler = fopen(path.c_str(), "r"); + if(handler) + { + fseek(handler, 0, SEEK_END); + string_size = ftell(handler); + rewind(handler); + + buffer = static_cast(malloc(sizeof(char) * (string_size + 1))); + + read_size = fread(buffer, sizeof(char), string_size, handler); + buffer[string_size] = '\0'; + + if(string_size != read_size) + { + free(buffer); + + size = 0; + return nullptr; + } + + size = read_size; + + fclose(handler); + + return buffer; + } + else + { + size = 0; + return nullptr; + } + } + } + + static void FreeFileHandle(File handle) + { + //don't delete physfs' file pointers! + if(!Filesystem::IsPacked()) + delete handle; + } + + //platform-specific file methods + static void CreateDirectory(const std::string& path); + static void CopyFile(const std::string& src, const std::string& dst, bool overwrite = false); + static void CopyDirectory(const std::string& src, const std::string& dst, bool overwrite = false); + + static void Remove(const std::string& path); + + static std::string Canonical(const std::string& path); + static std::vector DirectoryContents(const std::string& directory); + + static bool DirectoryExists(const std::string& path); + static bool FileExists(const std::string& path); +}; diff --git a/engine/platform/common/gfx.hpp b/engine/platform/common/gfx.hpp new file mode 100644 index 0000000..77e1ce1 --- /dev/null +++ b/engine/platform/common/gfx.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +class GFX +{ +public: + static void MakeContextCurrent(); + static void SwapBuffers(); + + static void GoFullscreen(); + static void ExitFullscreen(); + + static VkSurfaceKHR CreateVulkanSurface(VkInstance instance); + + static int& GetScreenWidth() + { + static int screenWidth = 1; + + return screenWidth; + } + + static int& GetScreenHeight() + { + static int screenHeight = 1; + + return screenHeight; + } + + static float GetScreenAspectRatio() + { + return GetScreenWidth() / static_cast(GetScreenHeight()); + } +}; \ No newline at end of file diff --git a/engine/platform/common/inputpump.hpp b/engine/platform/common/inputpump.hpp new file mode 100644 index 0000000..1a10886 --- /dev/null +++ b/engine/platform/common/inputpump.hpp @@ -0,0 +1,9 @@ +#pragma once + +class InputPump +{ +public: + static void Initialize(); + + static void PollEvents(); +}; \ No newline at end of file diff --git a/engine/platform/common/platform.hpp b/engine/platform/common/platform.hpp new file mode 100644 index 0000000..7a24fec --- /dev/null +++ b/engine/platform/common/platform.hpp @@ -0,0 +1,25 @@ +#pragma once + +class Platform +{ +public: + //used to pass windowing handles + static void SetUserData(void* pointer) + { + m_pointer = pointer; + } + + static void* GetUserData() + { + return m_pointer; + } + + //random misc functions + static void WarpMouse(double x, double y); + static float GetTime(); + + static void* GetProcAddress(const char* procname); + +private: + static void* m_pointer; +}; \ No newline at end of file diff --git a/engine/platform/emscripten/filesystem.cpp b/engine/platform/emscripten/filesystem.cpp new file mode 100644 index 0000000..cc495bd --- /dev/null +++ b/engine/platform/emscripten/filesystem.cpp @@ -0,0 +1,36 @@ +#include "filesystem.hpp" + +#include +#include +#include + +bool Filesystem::DirectoryExists(const std::string& path) +{ + DIR* dir = opendir(path.c_str()); + + return dir != nullptr; +} + +bool Filesystem::FileExists(const std::string& path) +{ + return access(path.c_str(), F_OK) != -1; +} + +std::vector Filesystem::DirectoryContents(const std::string& directory) +{ + std::vector tmp; + + DIR* dir; + dirent* ent; + if ((dir = opendir(directory.c_str())) != nullptr) { + while ((ent = readdir (dir)) != nullptr) { + if(ent->d_name != "." && ent->d_name != "..") + { + tmp.push_back(ent->d_name); + } + } + closedir (dir); + } + + return tmp; +} \ No newline at end of file diff --git a/engine/platform/emscripten/gfx.cpp b/engine/platform/emscripten/gfx.cpp new file mode 100644 index 0000000..5c8c06b --- /dev/null +++ b/engine/platform/emscripten/gfx.cpp @@ -0,0 +1,18 @@ +#include "gfx.hpp" + +#include +#include + +void GFX::MakeContextCurrent() +{ + SDL_GLContext glcontext = SDL_GL_CreateContext(static_cast(Platform::GetUserData())); +} + +void GFX::SwapBuffers() +{ + SDL_GL_SwapBuffers(); +} + +void GFX::GoFullscreen() {} + +void GFX::ExitFullscreen() {} \ No newline at end of file diff --git a/engine/platform/emscripten/inputpump.cpp b/engine/platform/emscripten/inputpump.cpp new file mode 100644 index 0000000..ec9205e --- /dev/null +++ b/engine/platform/emscripten/inputpump.cpp @@ -0,0 +1,11 @@ +#include "inputpump.hpp" + +void InputPump::PollEvents() +{ + +} + +void InputPump::Initialize() +{ + +} \ No newline at end of file diff --git a/engine/platform/emscripten/platform.cpp b/engine/platform/emscripten/platform.cpp new file mode 100644 index 0000000..935bf0b --- /dev/null +++ b/engine/platform/emscripten/platform.cpp @@ -0,0 +1,20 @@ +#include "platform.hpp" + +#include + +void* Platform::m_pointer; + +void* Platform::GetProcAddress(const char* procname) +{ + return SDL_GL_GetProcAddress(procname); +} + +void Platform::WarpMouse(double x, double y) +{ + +} + +float Platform::GetTime() +{ + +} \ No newline at end of file diff --git a/engine/platform/glfw/gfx.cpp b/engine/platform/glfw/gfx.cpp new file mode 100644 index 0000000..b5561a2 --- /dev/null +++ b/engine/platform/glfw/gfx.cpp @@ -0,0 +1,46 @@ +#include "gfx.hpp" + +#include + +#include "platform.hpp" + +int oldWindowX = 0, oldWindowY = 0; + +void GFX::MakeContextCurrent() +{ + glfwMakeContextCurrent(static_cast(Platform::GetUserData())); + glfwSwapInterval(1); +} + +void GFX::SwapBuffers() +{ + glfwSwapBuffers(static_cast(Platform::GetUserData())); +} + +void GFX::GoFullscreen() +{ + GLFWwindow* window = static_cast(Platform::GetUserData()); + + glfwGetWindowPos(window, &oldWindowX, &oldWindowY); //save window pos for later + const GLFWvidmode* videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + glfwSetWindowMonitor(window, glfwGetPrimaryMonitor(), 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); +} + +void GFX::ExitFullscreen() +{ + GLFWwindow* window = static_cast(Platform::GetUserData()); + + if(oldWindowX == 0 || oldWindowY == 0) + glfwGetWindowPos(window, &oldWindowX, &oldWindowY); //save window pos + + glfwSetWindowMonitor(window, nullptr, oldWindowX, oldWindowY, 1600, 900, GLFW_DONT_CARE); +} + +VkSurfaceKHR GFX::CreateVulkanSurface(VkInstance instance) +{ + VkSurfaceKHR surface; + glfwCreateWindowSurface(instance, static_cast(Platform::GetUserData()), nullptr, &surface); + + return surface; +} \ No newline at end of file diff --git a/engine/platform/glfw/inputpump.cpp b/engine/platform/glfw/inputpump.cpp new file mode 100644 index 0000000..f04313b --- /dev/null +++ b/engine/platform/glfw/inputpump.cpp @@ -0,0 +1,110 @@ +#include "inputpump.hpp" + +#include +#include +#include +#include +#include + +const std::map translatekey = { + { GLFW_KEY_A, Key::A }, + { GLFW_KEY_B, Key::B }, + { GLFW_KEY_C, Key::C }, + { GLFW_KEY_D, Key::D }, + { GLFW_KEY_E, Key::E }, + { GLFW_KEY_F, Key::F }, + { GLFW_KEY_G, Key::G }, + { GLFW_KEY_H, Key::H }, + { GLFW_KEY_I, Key::I }, + { GLFW_KEY_J, Key::J }, + { GLFW_KEY_K, Key::K }, + { GLFW_KEY_L, Key::L }, + { GLFW_KEY_M, Key::M }, + { GLFW_KEY_N, Key::N }, + { GLFW_KEY_O, Key::O }, + { GLFW_KEY_P, Key::P }, + { GLFW_KEY_Q, Key::Q }, + { GLFW_KEY_R, Key::R }, + { GLFW_KEY_S, Key::S }, + { GLFW_KEY_T, Key::T }, + { GLFW_KEY_U, Key::U }, + { GLFW_KEY_V, Key::V }, + { GLFW_KEY_W, Key::W }, + { GLFW_KEY_X, Key::X }, + { GLFW_KEY_Y, Key::Y }, + { GLFW_KEY_Z, Key::Z }, + { GLFW_KEY_TAB, Key::Tab }, + { GLFW_KEY_UP, Key::UpArrow }, + { GLFW_KEY_LEFT, Key::LeftArrow }, + { GLFW_KEY_RIGHT, Key::RightArrow }, + { GLFW_KEY_DOWN, Key::DownArrow }, + { GLFW_KEY_BACKSPACE, Key::Backspace }, + { GLFW_KEY_ENTER, Key::Enter }, + { GLFW_KEY_LEFT_CONTROL, Key::LeftControl }, + { GLFW_KEY_RIGHT_CONTROL, Key::RightControl }, + { GLFW_KEY_LEFT_SHIFT, Key::LeftShift }, + { GLFW_KEY_RIGHT_SHIFT, Key::RightShift }, + { GLFW_KEY_LEFT_ALT, Key::LeftAlt }, + { GLFW_KEY_RIGHT_ALT, Key::RightAlt }, + { GLFW_KEY_LEFT_SUPER, Key::LeftSuper }, + { GLFW_KEY_RIGHT_SUPER, Key::RightSuper } +}; + +static void cursor_pos_callback(GLFWwindow*, double xpos, double ypos) +{ + Input::FeedMousePosition(static_cast(xpos), static_cast(ypos)); +} + +void mouse_button_callback(GLFWwindow*, int button, int action, int) +{ + switch(button) + { + case GLFW_MOUSE_BUTTON_LEFT: + Input::FeedMouseState(MouseButton::Left, action); + break; + case GLFW_MOUSE_BUTTON_RIGHT: + Input::FeedMouseState(MouseButton::Right, action); + break; + default: + break; + } +} + +void scroll_callback(GLFWwindow*, double, double yoffset) +{ + Input::FeedScroll(yoffset); +} + +void key_callback(GLFWwindow*, int key, int, int action, int) +{ + if(!translatekey.count(key)) + return; + + Input::FeedKeyState(translatekey.at(key), action); +} + +void char_callback(GLFWwindow*, unsigned int c) +{ + Input::FeedChar(c); +} + +void InputPump::Initialize() +{ + Log::Print("Setting up input callbacks..."); + + GLFWwindow* window = static_cast(Platform::GetUserData()); + + glfwSetCursorPosCallback(window, cursor_pos_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetScrollCallback(window, scroll_callback); + glfwSetKeyCallback(window, key_callback); + glfwSetCharCallback(window, char_callback); + + glfwSetInputMode(window, GLFW_STICKY_KEYS, 1); + //glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, 1); +} + +void InputPump::PollEvents() +{ + glfwPollEvents(); +} \ No newline at end of file diff --git a/engine/platform/glfw/platform.cpp b/engine/platform/glfw/platform.cpp new file mode 100644 index 0000000..7750e8c --- /dev/null +++ b/engine/platform/glfw/platform.cpp @@ -0,0 +1,15 @@ +#include "platform.hpp" + +#include + +void* Platform::m_pointer; + +void Platform::WarpMouse(double x, double y) +{ + glfwSetCursorPos(static_cast(Platform::GetUserData()), x, y); +} + +float Platform::GetTime() +{ + return static_cast(glfwGetTime()); +} \ No newline at end of file diff --git a/engine/platform/unix/filesystem.cpp b/engine/platform/unix/filesystem.cpp new file mode 100644 index 0000000..752c91e --- /dev/null +++ b/engine/platform/unix/filesystem.cpp @@ -0,0 +1,113 @@ +#include "filesystem.hpp" + +#include +#include +#include +#include +#include +#include +#include + +void Filesystem::CreateDirectory(const std::string& path) +{ + mkdir(path.c_str(), 0755); +} + +void Filesystem::CopyFile(const std::string& src, const std::string& dst, bool overwrite) +{ + int read_fd; + int write_fd; + struct stat stat_buf; + off_t offset = 0; + + read_fd = open(src.c_str(), O_RDONLY); + fstat (read_fd, &stat_buf); + + int wflags = O_WRONLY | O_CREAT; + if(overwrite) + wflags |= O_TRUNC; + + write_fd = open(dst.c_str(), wflags, stat_buf.st_mode); + + sendfile(write_fd, read_fd, &offset, stat_buf.st_size); + + close(read_fd); + close(write_fd); +} + +void Filesystem::CopyDirectory(const std::string& src, const std::string& dst, bool overwrite) +{ + CreateDirectory(dst); + + char* paths[] = { const_cast(src.c_str()), 0 }; + FTS* tree = fts_open(paths, FTS_NOCHDIR, 0); + + FTSENT *node; + while ((node = fts_read(tree))) + { + if(node->fts_level > 0 && node->fts_name[0] == '.') + { + fts_set(tree, node, FTS_SKIP); + } + else if(node->fts_info & FTS_F) + { + CopyFile(node->fts_path, dst + Utility::RemoveSubstrings(node->fts_path, { src }), overwrite); + } + else if(node->fts_info & FTS_D) + { + CreateDirectory(dst + Utility::RemoveSubstrings(node->fts_path, { src })); + } + } + + fts_close(tree); +} + +void Filesystem::Remove(const std::string& path) +{ + unlink(path.c_str()); +} + +std::string Filesystem::Canonical(const std::string& path) +{ + char* n = new char[PATH_MAX]; + realpath(path.c_str(), n); + + return n; +} + +std::vector Filesystem::DirectoryContents(const std::string& directory) +{ + std::vector tmp; + + char* paths[] = { const_cast(directory.c_str()), 0 }; + FTS* tree = fts_open(paths, FTS_NOCHDIR, 0); + + FTSENT *node; + while ((node = fts_read(tree))) + { + if(node->fts_level > 0 && node->fts_name[0] == '.') + { + fts_set(tree, node, FTS_SKIP); + } + else if(node->fts_info & FTS_F) + { + tmp.push_back(std::string(node->fts_accpath)); + } + } + + fts_close(tree); + + return tmp; +} + +bool Filesystem::DirectoryExists(const std::string& path) +{ + DIR* dir = opendir(path.c_str()); + + return dir != nullptr; +} + +bool Filesystem::FileExists(const std::string& path) +{ + return access(path.c_str(), F_OK) != -1; +} \ No newline at end of file diff --git a/engine/platform/windows/filesystem.cpp b/engine/platform/windows/filesystem.cpp new file mode 100644 index 0000000..7f936af --- /dev/null +++ b/engine/platform/windows/filesystem.cpp @@ -0,0 +1,39 @@ +#include "filesystem.hpp" + +#define WIN32_LEAN_AND_MEAN +#include +#undef CopyFile + +bool Filesystem::DirectoryExists(const std::string& path) +{ + DWORD ftyp = GetFileAttributesA(path.c_str()); + + return ftyp != INVALID_FILE_ATTRIBUTES && ftyp & FILE_ATTRIBUTE_DIRECTORY; +} + +bool Filesystem::FileExists(const std::string& path) +{ + DWORD ftyp = GetFileAttributesA(path.c_str()); + + return ftyp != INVALID_FILE_ATTRIBUTES && !(ftyp & FILE_ATTRIBUTE_DIRECTORY); +} + +std::vector Filesystem::DirectoryContents(const std::string& directory) +{ + std::vector tmp; + + //TODO: implement windows directory contents listing + + return tmp; +} + +void Filesystem::CopyFile(const std::string& src, const std::string& dst, bool overwrite) +{ + //TODO: implement windows copyfile +} + +std::string Filesystem::Canonical(const std::string& path) +{ + //TODO: implement windows canonicallize path + return path; +} \ No newline at end of file diff --git a/engine/renderer/CMakeLists.txt b/engine/renderer/CMakeLists.txt new file mode 100644 index 0000000..cd44cef --- /dev/null +++ b/engine/renderer/CMakeLists.txt @@ -0,0 +1,17 @@ +set(SOURCE_FILES + src/debugpass.cpp + src/deferredpipeline.cpp + src/drawlist.cpp + src/imgui_wrapper.cpp + src/primitives.cpp + src/shadercompiler.cpp + src/shadowpass.cpp + src/skyboxpass.cpp + src/renderer.cpp) + +add_library(Renderer ${SOURCE_FILES}) +target_link_libraries(Renderer ECS ImGui glslang OGLCompiler SPIRV OSDependent pthread Platform ${Vulkan_LIBRARIES}) + +include_directories(include) +include_directories(${UTILITY_INCLUDE_DIR} ${CORE_INCLUDE_DIR} ${ASSETS_INCLUDE_DIR} ${ECS_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} ${INPUT_INCLUDE_DIR}) +include_directories(SYSTEM ${LIBRARY_JSON_INCLUDE_DIR} ${LIBRARY_GLSLANG_INCLUDE_DIR} ${LIBRARY_IMGUI_INCLUDE_DIR} ${LIBRARY_STB_INCLUDE_DIR}) \ No newline at end of file diff --git a/engine/renderer/include/debugpass.hpp b/engine/renderer/include/debugpass.hpp new file mode 100644 index 0000000..4ce5bce --- /dev/null +++ b/engine/renderer/include/debugpass.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include + +struct DebugImage +{ + VkImage image; + VkDeviceMemory memory; + VkImageView view; + VkSampler sampler; + + VkDescriptorSet descriptorSet; + + VkBuffer uniformMatrixBuffer, uniformDebugBuffer; + VkDeviceMemory uniformMatrixBufferMemory, uniformDebugBufferMemory; +}; + +class DebugPass +{ +public: + void Initialize(VkRenderPass renderPass, vk::Device device); + void Record(Camera* camera, VkCommandBuffer buffer, vk::Device device); + void Cleanup(vk::Device device); + + void CreateDebugImage(const char* filepath, DebugImage& image, vk::Device device); + void DestroyDebugImage(DebugImage& image, vk::Device device); + + void UploadUniformData(int index, + glm::vec3 position, + glm::vec3 color, + Camera* camera, + VkDeviceMemory matrixMemory, + VkDeviceMemory debugMemory, + uint32_t offsets[2], + vk::Device device); + + VkShaderModule vertexShaderModule, fragmentShaderModule; + + struct UniformMatrixData + { + glm::mat4 view; + glm::mat4 proj; + glm::mat4 model; + glm::vec4 color; + } matrixData[10]; + + struct UniformDebugData + { + glm::vec4 color; + glm::vec3 dummy; + int dummy2; + }; + + DebugImage lightImage, cameraImage; + + VkDescriptorSetLayout descriptorSetLayout; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + + VkDescriptorPool descriptorPool; +}; \ No newline at end of file diff --git a/engine/renderer/include/deferredpipeline.hpp b/engine/renderer/include/deferredpipeline.hpp new file mode 100644 index 0000000..423bd63 --- /dev/null +++ b/engine/renderer/include/deferredpipeline.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include +#define GLM_DEPTH_CLIP_SPACE GLM_DEPTH_NEGATIVE_ONE_TO_ONE +#include +#include + +class Material; + + +struct StaticDrawCommand +{ + VkBuffer indirectStateBuffer; + VkDeviceMemory indirectStateBufferMemory; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; + + VkBuffer modelBuffer; + VkDeviceMemory modelBufferMemory; + + Material* material; + uint32_t numGeometry; +}; + +struct Attachment +{ + VkImage image; + VkDeviceMemory memory; + VkImageView view; + VkFormat format; +}; + +class DeferredPipeline : public Pipeline +{ +public: + DeferredPipeline(Renderer* renderer); + ~DeferredPipeline() override ; + + void Record(VkCommandBuffer commandBuffer, uint32_t index) override; + void Submit(SubmitInfo si) override; + +private: + void SetupVertexDescriptions(); + void PrepareGBuffer(); + void PrepareLighting(); + void PreparePresentation(); + void SetupDescriptorLayout(); + void PreparePipelines(); + void SetupDescriptorPool(); + void SetupDescriptorSets(); + + void CreateSet(Material* material); + + void CreateAttachment(VkFormat format, VkImageUsageFlagBits usage, Attachment& attachment); + void DestroyAttachment(Attachment& attachment); + + void ClearStaticGeometryBuffers(); + + void RecordGBuffer(); + void RecordLighting(); + + struct + { + VkImage image; + VkDeviceMemory memory; + VkSampler sampler; + VkImageView view; + } m_placeholderImage; + + //uniform structs + struct UniformObjectData + { + glm::mat4 projection, view, model; + glm::vec4 albedoTint; + glm::vec4 emission; + float metallic, roughness; + }; + + struct UniformLightData + { + glm::mat4 projection, view, model; + glm::vec4 cameraPosition, lightPosition; + glm::vec3 color; + float radius; + glm::vec2 viewport; + }; + + struct UniformPostData + { + float exposure = 1.0f, gamma = 2.2f; + }; + + VkBuffer m_uniformObjectBuffer, m_uniformLightBuffer, m_uniformPostBuffer; + VkDeviceMemory m_uniformObjectBufferMemory, m_uniformLightBufferMemory, m_uniformPostBufferMemory; + + VkDescriptorPool m_descriptorPool; + VkDescriptorSet m_ambientDescriptorSet, m_lightingDescriptorSet, m_postDescriptorSet; + + struct + { + VkFramebuffer framebuffer; + Attachment position, normal, albedo, material, depth; + + VkSampler colorSampler; + } m_gBuffer; + + struct + { + VkFramebuffer framebuffer; + Attachment color; + + VkSampler colorSampler; + } m_finalResult; + + struct + { + VkRenderPass gBuffer, lighting, presentation; + } m_renderPasses; + + struct + { + VkDescriptorSetLayout gBuffer, ambientLighting, pointLighting, postProcessing; + } m_descriptorLayouts; + + struct + { + VkPipelineLayout gBuffer, ambientLighting, pointLighting, postProcessing; + } m_pipelineLayouts; + + struct + { + VkPipeline geometryDynamic, geometryStatic, ambientLighting, pointLighting, postProcessing; + } m_pipelines; + + struct + { + VkPipelineVertexInputStateCreateInfo geometryDynamic, geometryStatic, quad; + } m_inputStates; + + VkCommandBuffer m_gBufferCommandBuffer, m_lightingCommandBuffer; + VkSemaphore m_gbufferFinishedSemaphore, m_lightingFinishedSemaphore; + + std::vector m_swapchainFramebuffers; + std::map m_samplerSets; + std::vector m_staticDrawCommands; + + Renderer* m_renderer; +}; \ No newline at end of file diff --git a/engine/renderer/include/drawlist.hpp b/engine/renderer/include/drawlist.hpp new file mode 100644 index 0000000..a18f56e --- /dev/null +++ b/engine/renderer/include/drawlist.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +struct DrawCommand +{ + ~DrawCommand() + { + geometryData.clear(); + + indices.clear(); + vertices.clear(); + models.clear(); + } + + struct Data + { + int vertexCount; + int instanceCount; + int firstVertex; + int baseVertex; + int baseInstance; + }; + + std::vector geometryData; + + std::vector indices; + std::vector vertices; + std::vector models; +}; + +struct StaticGeometry +{ + std::map drawCommands; +}; + +//reserved for future use +struct InstancedGeometry {}; + +struct DynamicGeometry +{ + std::string name; + Mesh* mesh; + Material* material; + glm::mat4 model; +}; + +class DrawList +{ +public: + void RebuildDynamicLists(); + void RebuildStaticLists(); + + static void InvalidateNextFrame(); + + StaticGeometry GetStaticGeometry(); + std::vector GetInstancedGeometry(); + std::vector GetDynamicGeometry(); + + static std::function onStaticGeometryUpdate; + +private: + std::vector m_dynamicGeometry; + StaticGeometry m_staticGeometry; + + static bool m_frameInvalidated; +}; \ No newline at end of file diff --git a/engine/renderer/include/imgui_wrapper.hpp b/engine/renderer/include/imgui_wrapper.hpp new file mode 100644 index 0000000..7d780da --- /dev/null +++ b/engine/renderer/include/imgui_wrapper.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +class ImGuiWrapper +{ +public: + ImGuiWrapper(vk::Device& device); + ~ImGuiWrapper(); + + void NewFrame(); + void RenderDrawLists(); + +private: + void MouseButtonCallback(int button, int action); + + vk::Device m_device; + + bool m_mousePressed[3]; +}; diff --git a/engine/renderer/include/pipeline.hpp b/engine/renderer/include/pipeline.hpp new file mode 100644 index 0000000..8a33d85 --- /dev/null +++ b/engine/renderer/include/pipeline.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + + +struct SubmitInfo +{ + //image available semaphore + VkSemaphore ias; + + //current command buffer + VkCommandBuffer cmdBuf; + + VkFence submitFence; +}; + +class Pipeline +{ +public: + virtual ~Pipeline() {} + + //record into command buffer + virtual void Record(VkCommandBuffer, uint32_t) {} + + //submit work + virtual void Submit(SubmitInfo) {} + + //we require very pipeline to have a render finished semaphore + VkSemaphore m_renderFinishedSemaphore = VK_NULL_HANDLE; +}; \ No newline at end of file diff --git a/engine/renderer/include/primitives.hpp b/engine/renderer/include/primitives.hpp new file mode 100644 index 0000000..139c42f --- /dev/null +++ b/engine/renderer/include/primitives.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +struct Sphere +{ + VkBuffer vertexBuffer, indexBuffer; + VkDeviceMemory vertexBufferMemory, indexBufferMemory; + + uint32_t indexCount; +}; + +struct Quad +{ + VkBuffer vertexBuffer, indexBuffer; + VkDeviceMemory vertexBufferMemory, indexBufferMemory; +}; + +class Primitives +{ +public: + static void Initialize(Renderer* renderer); + static void Cleanup(Renderer* renderer); + + static void DrawQuad(VkCommandBuffer commandBuffer); + static void DrawSphere(VkCommandBuffer commandBuffer); + + static Sphere sphere; + static Quad quad; +}; \ No newline at end of file diff --git a/engine/renderer/include/renderer.hpp b/engine/renderer/include/renderer.hpp new file mode 100644 index 0000000..6fbb0ee --- /dev/null +++ b/engine/renderer/include/renderer.hpp @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "drawlist.hpp" + +struct VulkanMesh +{ + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + + VkBuffer indexBuffer; + VkDeviceMemory indexBufferMemory; +}; + +struct VulkanTexture +{ + VkImage image; + VkDeviceMemory memory; + VkImageView view; + VkSampler sampler; +}; + +struct VulkanCubemap +{ + +}; + +namespace ResourceLimits +{ + constexpr size_t maxLights = 35; + constexpr size_t maxDynamicGeometry = 100; +}; + +struct RequestedFeatures +{ + bool debugPass = false; + bool imgui = false; + bool autoload = false; +}; + +struct RuntimeOptions +{ + bool vsync = false; +}; + +class Pipeline; +class ImGuiWrapper; +class Mesh; +class Texture; +class Cubemap; +class Engine; + +class Renderer +{ +public: + Renderer(Engine* engine, RequestedFeatures features); + ~Renderer(); + + void NewFrame(); + void Draw(); + + void Resized(); + void ChangeOptions(RuntimeOptions); + + DrawList& GetDrawList() + { + return m_drawList; + } + + //renderer resource operations + void SynthesizeMesh(Mesh* mesh); + void ReleaseMesh(Mesh* mesh); + + VulkanMesh GetMesh(Mesh* mesh); + + void SynthesizeTexture(Texture* texture); + void ReleaseTexture(Texture* texture); + + VulkanTexture GetTexture(Texture* texture); + + void SynthesizeCubemap(Cubemap* cubemap) {} + void ReleaseCubemap(Cubemap* cubemap) {} + + VulkanCubemap GetTexture(Cubemap* cubemap) {} + + void PrepareFrame(); + void SubmitFrame(); + + vk::Instance instance; + VkSurfaceKHR surface; + + vk::Device device; + + VkSwapchainKHR swapchain; + VkExtent2D swapchainExtent; + VkFormat swapchainImageFormat; + std::vector swapchainImages; + std::vector swapchainImageViews; + + Pipeline* pipeline; + + std::vector commandBuffers; + + VkSemaphore imageAvailableSemaphore; + + VkFence submitFence; + + std::function onRender; + std::function onRenderPassEnd; + + uint32_t imageIndex; + +private: + void CreateSwapchain(); + void AllocateCommandBuffers(); + + DrawList m_drawList; + Engine* m_engine; + + ImGuiWrapper* m_imWrapper; + + std::map m_vulkanMeshes; + std::map m_vulkanTextures; +}; diff --git a/engine/renderer/include/shadercompiler.hpp b/engine/renderer/include/shadercompiler.hpp new file mode 100644 index 0000000..6f3e60f --- /dev/null +++ b/engine/renderer/include/shadercompiler.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +class ShaderCompiler +{ +public: + static VkShaderModule CreateShaderModule(const std::string& path, VkDevice logicalDevice); +}; \ No newline at end of file diff --git a/engine/renderer/include/shadowpass.hpp b/engine/renderer/include/shadowpass.hpp new file mode 100644 index 0000000..1625ae7 --- /dev/null +++ b/engine/renderer/include/shadowpass.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include + +#include "renderer.hpp" + +struct ShadowData +{ + VkImage image; + VkDeviceMemory memory; + VkImageView view; + VkSampler sampler; + VkFramebuffer framebuffer; +}; + +class ShadowPass +{ +public: + void Initialize(Renderer* inRenderer, vk::Device device); + void Cleanup(vk::Device device); + + void Record(vk::Device device); + + bool IsLightPrepared(Light* light); + + ShadowData GetLightShadow(Light* light); + + void PrepareLight(Light* light, vk::Device device); + + bool HasRecorded() + { + return has_recorded; + } + + VkCommandBuffer commandBuffer; + VkSemaphore semaphore; + +private: + bool has_recorded = false; + + VkRenderPass renderPass; + + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + + VkShaderModule vertexShaderModule; + VkShaderModule fragmentShaderModule; + + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + + VkDescriptorPool descriptorPool; + + struct UniformObjectData + { + glm::mat4 model, view, proj; + }; + + VkBuffer uniformObjectBuffer; + VkDeviceMemory uniformObjectBufferMemory; + + std::map lights; + + Renderer* renderer; +}; \ No newline at end of file diff --git a/engine/renderer/include/skyboxpass.hpp b/engine/renderer/include/skyboxpass.hpp new file mode 100644 index 0000000..1c69daa --- /dev/null +++ b/engine/renderer/include/skyboxpass.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +class SkyboxPass { +public: + void Initialize(VkRenderPass renderPass, vk::Device device); + void Cleanup(vk::Device device); + + void Record(Camera *camera, VkCommandBuffer buffer, vk::Device device); + + VkShaderModule vertexShaderModule, fragmentShaderModule; + + struct UniformMatrixData + { + glm::mat4 proj, view; + }; + + struct UniformSceneData + { + glm::vec3 sunPosition; + }; + + struct + { + std::vector vertices = { + {-1.0f, 1.0f, -1.0f}, + {-1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + {1.0f, 1.0f, -1.0f}, + {-1.0f, 1.0f, -1.0f}, + + {-1.0f, -1.0f, 1.0f}, + {-1.0f, -1.0f, -1.0f}, + {-1.0f, 1.0f, -1.0f}, + {-1.0f, 1.0f, -1.0f}, + {-1.0f, 1.0f, 1.0f}, + {-1.0f, -1.0f, 1.0f}, + + {1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + + {-1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f}, + {-1.0f, -1.0f, 1.0f}, + + {-1.0f, 1.0f, -1.0f}, + {1.0f, 1.0f, -1.0f}, + {1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f}, + + {-1.0f, -1.0f, -1.0f}, + {-1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + {-1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f} + }; + + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + } cube; + + VkDescriptorSetLayout descriptorSetLayout; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + + VkBuffer uniformMatrixBuffer, uniformSceneBuffer; + VkDeviceMemory uniformMatrixBufferMemory, uniformSceneBufferMemory; + + VkDescriptorPool descriptorPool; + VkDescriptorSet descriptorSet; +}; \ No newline at end of file diff --git a/engine/renderer/include/vk.hpp b/engine/renderer/include/vk.hpp new file mode 100644 index 0000000..a1451ad --- /dev/null +++ b/engine/renderer/include/vk.hpp @@ -0,0 +1,334 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "vkinitializers.hpp" +#include "vkdevice.hpp" +#include "vkinstance.hpp" + +struct SwapChainSupportDetails +{ + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; +}; + +namespace vk +{ + inline uint32_t FindMemoryType(uint32_t typeFilter, + VkMemoryPropertyFlags properties, + VkPhysicalDeviceMemoryProperties memProperties) + { + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + return i; + + throw std::runtime_error("failed to find suitable memory type!"); + } + + inline void CreateBuffer(VkDeviceSize size, + VkBufferUsageFlags usage, + VkMemoryPropertyFlags properties, + VkBuffer& buffer, + VkDeviceMemory& memory, + Device& device) + { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(device.physicalDevice, &memProperties); + + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateBuffer(device.logicalDevice, &bufferInfo, VK_NULL_HANDLE, &buffer); + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device.logicalDevice, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties, memProperties); + + vkAllocateMemory(device.logicalDevice, &allocInfo, VK_NULL_HANDLE, &memory); + + vkBindBufferMemory(device.logicalDevice, buffer, memory, 0); + } + + inline VkCommandBuffer beginSingleTimeCommands(Device& device) + { + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = device.commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device.logicalDevice, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + return commandBuffer; + } + + inline void endSingleTimeCommands(VkCommandBuffer commandBuffer, Device& device) + { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(device.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(device.graphicsQueue); + + vkFreeCommandBuffers(device.logicalDevice, device.commandPool, 1, &commandBuffer); + } + + inline void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size, Device& device) + { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(device); + + VkBufferCopy copyRegion = {}; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + endSingleTimeCommands(commandBuffer, device); + } + + inline void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, Device& device) + { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(device); + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, 1, &barrier); + + endSingleTimeCommands(commandBuffer, device); + } + + inline void createImage(uint32_t width, + uint32_t height, + VkFormat format, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkMemoryPropertyFlags properties, + VkImage& image, + VkDeviceMemory& imageMemory, + Device& device) + { + VkImageCreateInfo imageInfo = {}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = format; + imageInfo.tiling = tiling; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imageInfo.usage = usage; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateImage(device.logicalDevice, &imageInfo, VK_NULL_HANDLE, &image); + + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(device.physicalDevice, &memProperties); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(device.logicalDevice, image, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties, memProperties); + + vkAllocateMemory(device.logicalDevice, &allocInfo, VK_NULL_HANDLE, &imageMemory); + + vkBindImageMemory(device.logicalDevice, image, imageMemory, 0); + } + + inline void copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height, Device& device) + { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(device); + + VkImageSubresourceLayers subResource = {}; + subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subResource.baseArrayLayer = 0; + subResource.mipLevel = 0; + subResource.layerCount = 1; + + VkImageCopy region = {}; + region.srcSubresource = subResource; + region.dstSubresource = subResource; + region.srcOffset = {0, 0, 0}; + region.dstOffset = {0, 0, 0}; + region.extent.width = width; + region.extent.height = height; + region.extent.depth = 1; + + vkCmdCopyImage(commandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + endSingleTimeCommands(commandBuffer, device); + } + + inline uint32_t GetUniformOffsetAlignment(VkPhysicalDevice physicalDevice) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + + return properties.limits.minUniformBufferOffsetAlignment; + } + + inline std::vector GetPhysicalDevices(VkInstance instance) + { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, VK_NULL_HANDLE); + + std::vector physicalDevices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data()); + + return physicalDevices; + } + + inline SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) + { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, VK_NULL_HANDLE); + + if (formatCount != 0) + { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, VK_NULL_HANDLE); + + if (presentModeCount != 0) + { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + inline VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector& availableFormats) + { + if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) + { + return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + } + + for (const auto& availableFormat : availableFormats) + { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + return availableFormat; + } + } + + return availableFormats[0]; + } + + inline VkPresentModeKHR ChooseSwapPresentMode(const std::vector availablePresentModes) + { + for (const auto& availablePresentMode : availablePresentModes) + { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_IMMEDIATE_KHR; + } + + inline VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) + { + if (capabilities.currentExtent.width != std::numeric_limits::max()) + { + return capabilities.currentExtent; + } + else + { + VkExtent2D actualExtent = { 640 , 480 }; + + actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); + + return actualExtent; + } + } + + inline std::vector GetQueueFamilies(VkPhysicalDevice physicalDevice) + { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, VK_NULL_HANDLE); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + + return queueFamilies; + } + + inline void BootstrapImage(VkImage& image, VkDeviceMemory& memory, Device& device) + { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(device.physicalDevice, &memProperties); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(device.logicalDevice, image, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = vk::FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memProperties); + + vkAllocateMemory(device.logicalDevice, &allocInfo, VK_NULL_HANDLE, &memory); + + vkBindImageMemory(device.logicalDevice, image, memory, 0); + } +} diff --git a/engine/renderer/include/vkdevice.hpp b/engine/renderer/include/vkdevice.hpp new file mode 100644 index 0000000..fb51b00 --- /dev/null +++ b/engine/renderer/include/vkdevice.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace vk +{ + struct Device + { + Device() {} + + Device(VkPhysicalDevice inPhysicalDevice) + { + physicalDevice = inPhysicalDevice; + + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr); + + std::vector extensions(extensionCount); + vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, extensions.data()); + + for(auto extension : extensions) + supportedExtensions.push_back(extension.extensionName); + } + + ~Device() + { + if(logicalDevice != VK_NULL_HANDLE) + { + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); + + vkDestroyDevice(logicalDevice, nullptr); + } + } + + void CreateLogical(VkPhysicalDeviceFeatures enabledDeviceFeatures = VkPhysicalDeviceFeatures()) + { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + for(uint32_t i = 0; i < queueFamilies.size(); i++) + { + if(queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + queueFamilyIndices.graphics = i; + } + else if(queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) + { + queueFamilyIndices.transfer = i; + } + } + + std::array queueCreateInfos = { + vk::initializers::DeviceQueue(queueFamilyIndices.graphics), + vk::initializers::DeviceQueue(queueFamilyIndices.transfer) + }; + + std::array enabledExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); + deviceCreateInfo.pEnabledFeatures = &enabledDeviceFeatures; + + vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice); + + vkGetDeviceQueue(logicalDevice, queueFamilyIndices.graphics, 0, &graphicsQueue); + vkGetDeviceQueue(logicalDevice, queueFamilyIndices.transfer, 0, &transferQueue); + + VkCommandPoolCreateInfo commandPoolCreateInfo = {}; + commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndices.graphics; + + vkCreateCommandPool(logicalDevice, &commandPoolCreateInfo, nullptr, &commandPool); + } + + bool IsExtensionSupported(const std::string& extension) + { + return std::find(supportedExtensions.begin(), supportedExtensions.end(), extension) != supportedExtensions.end(); + } + + VkDevice logicalDevice = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice; + + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + + std::vector supportedExtensions; + + struct + { + uint32_t graphics, transfer; + } queueFamilyIndices; + + VkQueue graphicsQueue, transferQueue; + + VkCommandPool commandPool; + }; +} \ No newline at end of file diff --git a/engine/renderer/include/vkinitializers.hpp b/engine/renderer/include/vkinitializers.hpp new file mode 100644 index 0000000..29c6314 --- /dev/null +++ b/engine/renderer/include/vkinitializers.hpp @@ -0,0 +1,286 @@ +#pragma once + +#include +#include + +namespace vk +{ + namespace initializers + { + inline VkViewport Viewport(uint32_t width, uint32_t height) + { + VkViewport viewport = {}; + viewport.width = static_cast(width); //why does vulkan define these as floats? how do you have half a pixel? + viewport.height = static_cast(height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + return viewport; + } + + inline VkRect2D Rect2D(uint32_t width, uint32_t height, int32_t x, int32_t y) + { + VkRect2D rect2D = {}; + rect2D.extent = { width, height }; + rect2D.offset = { x, y }; + + return rect2D; + } + + inline VkDescriptorSetLayoutBinding DescriptorBinding(uint32_t binding, VkDescriptorType type, VkShaderStageFlags stage) + { + VkDescriptorSetLayoutBinding descriptorSetLayoutBinding = {}; + descriptorSetLayoutBinding.binding = binding; + descriptorSetLayoutBinding.descriptorType = type; + descriptorSetLayoutBinding.descriptorCount = 1; + descriptorSetLayoutBinding.stageFlags = stage; + + return descriptorSetLayoutBinding; + } + + inline VkPipelineShaderStageCreateInfo ShaderStage(VkShaderStageFlagBits stage, VkShaderModule shaderModule) + { + VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {}; + shaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCreateInfo.stage = stage; + shaderStageCreateInfo.module = shaderModule; + shaderStageCreateInfo.pName = "main"; + shaderStageCreateInfo.pSpecializationInfo = nullptr; + + return shaderStageCreateInfo; + } + + inline VkPipelineColorBlendStateCreateInfo ColorBlendState(uint32_t count, VkPipelineColorBlendAttachmentState* attachments) + { + VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {}; + colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCreateInfo.logicOpEnable = VK_FALSE; + colorBlendStateCreateInfo.logicOp = VK_LOGIC_OP_COPY; + colorBlendStateCreateInfo.attachmentCount = count; + colorBlendStateCreateInfo.pAttachments = attachments; + colorBlendStateCreateInfo.blendConstants[0] = 0.0f; + colorBlendStateCreateInfo.blendConstants[1] = 0.0f; + colorBlendStateCreateInfo.blendConstants[2] = 0.0f; + colorBlendStateCreateInfo.blendConstants[3] = 0.0f; + + return colorBlendStateCreateInfo; + } + + inline VkPipelineMultisampleStateCreateInfo MultisampleState() + { + VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {}; + multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE; + multisampleStateCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + return multisampleStateCreateInfo; + } + + inline VkPipelineViewportStateCreateInfo ViewportState() + { + VkPipelineViewportStateCreateInfo viewportStateCreateInfo = {}; + viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCreateInfo.viewportCount = 1; + viewportStateCreateInfo.scissorCount = 1; + + return viewportStateCreateInfo; + } + + inline VkPipelineInputAssemblyStateCreateInfo InputAssemblyState() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {}; + inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCreateInfo.pNext = nullptr; + inputAssemblyStateCreateInfo.flags = 0; + inputAssemblyStateCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE; + + return inputAssemblyStateCreateInfo; + } + + inline VkPipelineColorBlendAttachmentState ColorBlendAttachmentState(VkColorComponentFlags colorWriteMask, VkBool32 blendEnabled = VK_FALSE) + { + VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {}; + pipelineColorBlendAttachmentState.colorWriteMask = colorWriteMask; + pipelineColorBlendAttachmentState.blendEnable = blendEnabled; + + return pipelineColorBlendAttachmentState; + } + + inline VkPipelineDynamicStateCreateInfo DynamicState(uint32_t count, const VkDynamicState* states) + { + VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = {}; + pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + pipelineDynamicStateCreateInfo.pDynamicStates = states; + pipelineDynamicStateCreateInfo.dynamicStateCount = count; + + return pipelineDynamicStateCreateInfo; + } + + inline VkWriteDescriptorSet DescriptorWrite(VkDescriptorSet set, uint32_t binding, VkDescriptorType type, VkDescriptorBufferInfo* bufferInfo) + { + VkWriteDescriptorSet writeDescriptorSet = {}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.dstSet = set; + writeDescriptorSet.dstBinding = binding; + writeDescriptorSet.descriptorType = type; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.pBufferInfo = bufferInfo; + + return writeDescriptorSet; + } + + inline VkWriteDescriptorSet DescriptorWrite(VkDescriptorSet set, uint32_t binding, VkDescriptorType type, VkDescriptorImageInfo* imageInfo) + { + VkWriteDescriptorSet writeDescriptorSet = {}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.dstSet = set; + writeDescriptorSet.dstBinding = binding; + writeDescriptorSet.descriptorType = type; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.pImageInfo = imageInfo; + + return writeDescriptorSet; + } + + inline VkVertexInputAttributeDescription VertexAttribute(uint32_t binding, uint32_t location, VkFormat format, uint32_t offset = 0) + { + VkVertexInputAttributeDescription vertexInputAttributeDescription = {}; + vertexInputAttributeDescription.binding = binding; + vertexInputAttributeDescription.location = location; + vertexInputAttributeDescription.format = format; + vertexInputAttributeDescription.offset = offset; + + return vertexInputAttributeDescription; + } + + inline VkImageCreateInfo Image2D(VkFormat format, uint32_t width, uint32_t height, VkImageUsageFlags usage, VkImageLayout layout) + { + VkImageCreateInfo imageCreateInfo = {}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.format = format; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.initialLayout = layout; + imageCreateInfo.usage = usage; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + return imageCreateInfo; + } + + inline VkImageViewCreateInfo ImageView2D(VkFormat format, VkImageAspectFlags aspectMask, VkImage& image) + { + VkImageViewCreateInfo viewCreateInfo = {}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = format; + viewCreateInfo.subresourceRange = {}; + viewCreateInfo.subresourceRange.aspectMask = aspectMask; + viewCreateInfo.subresourceRange.baseMipLevel = 0; + viewCreateInfo.subresourceRange.levelCount = 1; + viewCreateInfo.subresourceRange.baseArrayLayer = 0; + viewCreateInfo.subresourceRange.layerCount = 1; + viewCreateInfo.image = image; + + return viewCreateInfo; + } + + //used normally for framebuffers where you need to sample it later + inline VkAttachmentDescription TypicalAttachment(VkFormat format, + VkImageLayout initialLayout, + VkImageLayout finalLayout) + { + VkAttachmentDescription attachmentDescription = {}; + attachmentDescription.format = format; + attachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescription.initialLayout = initialLayout; + attachmentDescription.finalLayout = finalLayout; + + return attachmentDescription; + } + + inline VkAttachmentReference TypicalColorReference(uint32_t attachment) + { + VkAttachmentReference attachmentReference = {}; + attachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentReference.attachment = attachment; + + return attachmentReference; + } + + inline VkPipelineRasterizationStateCreateInfo RasterizationState( + VkPolygonMode polygonMode, + VkCullModeFlags cullMode, + VkFrontFace frontFace, + VkPipelineRasterizationStateCreateFlags flags) + { + VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo = {}; + pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + pipelineRasterizationStateCreateInfo.polygonMode = polygonMode; + pipelineRasterizationStateCreateInfo.cullMode = cullMode; + pipelineRasterizationStateCreateInfo.frontFace = frontFace; + pipelineRasterizationStateCreateInfo.flags = flags; + pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE; + pipelineRasterizationStateCreateInfo.lineWidth = 1.0f; + + return pipelineRasterizationStateCreateInfo; + } + + inline VkPipelineDepthStencilStateCreateInfo DepthStencilState(VkBool32 depthTestEnable, + VkBool32 depthWriteEnable, + VkCompareOp depthCompare) + { + VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo = {}; + pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + pipelineDepthStencilStateCreateInfo.depthTestEnable = depthTestEnable; + pipelineDepthStencilStateCreateInfo.depthWriteEnable = depthWriteEnable; + pipelineDepthStencilStateCreateInfo.depthCompareOp = depthCompare; + pipelineDepthStencilStateCreateInfo.front = pipelineDepthStencilStateCreateInfo.back; + pipelineDepthStencilStateCreateInfo.back.compareOp = VK_COMPARE_OP_ALWAYS; + + return pipelineDepthStencilStateCreateInfo; + } + + inline VkVertexInputBindingDescription BindingDescription(uint32_t binding, uint32_t stride, VkVertexInputRate inputRate) + { + VkVertexInputBindingDescription bindingDescription = {}; + bindingDescription.binding = binding; + bindingDescription.stride = stride; + bindingDescription.inputRate = inputRate; + + return bindingDescription; + } + + inline VkDescriptorPoolSize PoolSize(VkDescriptorType type, uint32_t size) + { + VkDescriptorPoolSize poolSize = {}; + poolSize.descriptorCount = size; + poolSize.type = type; + + return poolSize; + } + + inline VkDeviceQueueCreateInfo DeviceQueue(uint32_t queueIndex) + { + constexpr float queuePriority = 0.0f; + + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + return queueCreateInfo; + } + } +} \ No newline at end of file diff --git a/engine/renderer/include/vkinstance.hpp b/engine/renderer/include/vkinstance.hpp new file mode 100644 index 0000000..b7f0e30 --- /dev/null +++ b/engine/renderer/include/vkinstance.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include + +static VkBool32 debugCallback(VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char* msg, void*) +{ + Log::Error("Vulkan Error: %s", msg); + + return VK_FALSE; +} + +namespace vk +{ + constexpr std::array enabledLayers = { "VK_LAYER_LUNARG_standard_validation" }; + constexpr uint32_t vulkanVersion = VK_MAKE_VERSION(1, 0, 3); + + class Instance + { + public: + Instance() + { + Log::Print("Built against Vulkan %s", VersionToString(vulkanVersion).c_str()); + + std::vector enabledExtensions; + + //TODO: implement required extensions api into GFX + enabledExtensions.push_back("VK_EXT_debug_report"); + enabledExtensions.push_back("VK_KHR_surface"); +#ifdef LINUX + enabledExtensions.push_back("VK_KHR_xcb_surface"); +#endif + + VkApplicationInfo applicationInfo = {}; + applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + applicationInfo.pApplicationName = ""; + applicationInfo.pEngineName = constants::engineName; + applicationInfo.apiVersion = vulkanVersion; + + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + instanceCreateInfo.enabledLayerCount = static_cast(enabledLayers.size()); + instanceCreateInfo.ppEnabledLayerNames = enabledLayers.data(); + instanceCreateInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); + + vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance); + + VkDebugReportCallbackCreateInfoEXT createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | + VK_DEBUG_REPORT_WARNING_BIT_EXT | + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + createInfo.pfnCallback = reinterpret_cast(debugCallback); + + CreateDebugReportCallback(m_instance, &createInfo, nullptr, &m_callback); + } + + ~Instance() + { + if(m_callback) + DestroyDebugReportCallback(m_instance, m_callback, nullptr); + + if(m_instance) + vkDestroyInstance(m_instance, nullptr); + } + + operator VkInstance&() + { + return m_instance; + } + + private: + VkResult CreateDebugReportCallback(VkInstance instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugReportCallbackEXT* pCallback) + { + auto func = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + if (func != nullptr) + { + return func(instance, pCreateInfo, pAllocator, pCallback); + } + else + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + + void DestroyDebugReportCallback(VkInstance instance, + VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks* pAllocator) + { + auto func = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + if (func != nullptr) + { + func(instance, callback, pAllocator); + } + } + + std::string VersionToString(uint32_t version) + { + std::stringstream ss; + ss << (version >> 22) << "." << ((version >> 12) & 0x3ff) << "." << (version & 0xfff); + + return ss.str(); + } + + VkInstance m_instance = VK_NULL_HANDLE; + VkDebugReportCallbackEXT m_callback = VK_NULL_HANDLE; + }; +} \ No newline at end of file diff --git a/engine/renderer/src/debugpass.cpp b/engine/renderer/src/debugpass.cpp new file mode 100644 index 0000000..94d2cf5 --- /dev/null +++ b/engine/renderer/src/debugpass.cpp @@ -0,0 +1,465 @@ +#include "debugpass.hpp" + +#include +#include +#include +#include +#include + +#include "shadercompiler.hpp" + +void DebugPass::Initialize(VkRenderPass renderPass, vk::Device device) +{ + vertexShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/debug.vert", device.logicalDevice); + fragmentShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/debug.frag", device.logicalDevice); + + VkDescriptorSetLayoutBinding matrixLayoutBinding = {}; + matrixLayoutBinding.binding = 0; + matrixLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + matrixLayoutBinding.descriptorCount = 1; + matrixLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding debugLayoutBinding = {}; + debugLayoutBinding.binding = 1; + debugLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + debugLayoutBinding.descriptorCount = 1; + debugLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding imageLayoutBinding = {}; + imageLayoutBinding.binding = 2; + imageLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + imageLayoutBinding.descriptorCount = 1; + imageLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding bindings[] = { matrixLayoutBinding, debugLayoutBinding, imageLayoutBinding }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 3; + layoutInfo.pBindings = bindings; + + vkCreateDescriptorSetLayout(device.logicalDevice, &layoutInfo, nullptr, &descriptorSetLayout); + + VkPipelineShaderStageCreateInfo vertexStageCreateInfo; + vertexStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexStageCreateInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexStageCreateInfo.module = vertexShaderModule; + vertexStageCreateInfo.pName = "main"; + vertexStageCreateInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo fragmentStageCreateInfo; + fragmentStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentStageCreateInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragmentStageCreateInfo.module = fragmentShaderModule; + fragmentStageCreateInfo.pName = "main"; + fragmentStageCreateInfo.pSpecializationInfo = nullptr; + + std::vector shaderStages = { vertexStageCreateInfo, fragmentStageCreateInfo }; + + VkVertexInputBindingDescription vertexBindingDescription; + vertexBindingDescription.binding = 0; + vertexBindingDescription.stride = sizeof(glm::vec3); + vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription vertexAttributeDescription; + vertexAttributeDescription.binding = 0; + vertexAttributeDescription.location = 0; + vertexAttributeDescription.format = VK_FORMAT_R32G32B32_SFLOAT; + vertexAttributeDescription.offset = 0; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.flags = 0; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &vertexAttributeDescription; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.flags = 0; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = GFX::GetScreenWidth(); + viewport.height = GFX::GetScreenHeight(); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor; + scissor.offset = { 0, 0 }; + scissor.extent.width = GFX::GetScreenWidth(); + scissor.extent.height = GFX::GetScreenHeight(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState brightBlendAttachment = {}; + brightBlendAttachment.colorWriteMask = 0; + brightBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState attachmentStates[] = { colorBlendAttachment, brightBlendAttachment }; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 2; + colorBlending.pAttachments = attachmentStates; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + + vkCreatePipelineLayout(device.logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout); + + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_FALSE; + depthStencil.depthWriteEnable = VK_FALSE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.stageCount = 2; + pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.pVertexInputState = &vertexInputInfo; + pipelineCreateInfo.pInputAssemblyState = &inputAssembly; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pRasterizationState = &rasterizer; + pipelineCreateInfo.pMultisampleState = &multisampling; + pipelineCreateInfo.pDepthStencilState = &depthStencil; + pipelineCreateInfo.pColorBlendState = &colorBlending; + pipelineCreateInfo.layout = pipelineLayout; + pipelineCreateInfo.renderPass = renderPass; + + vkCreateGraphicsPipelines(device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + + VkDescriptorPoolSize poolSize = {}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + poolSize.descriptorCount = 4; + + VkDescriptorPoolSize imagePoolSize = {}; + imagePoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + imagePoolSize.descriptorCount = 2; + + VkDescriptorPoolSize poolSizes[] = { poolSize, imagePoolSize }; + + VkDescriptorPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 2; + poolInfo.pPoolSizes = poolSizes; + poolInfo.maxSets = 2; + + vkCreateDescriptorPool(device.logicalDevice, &poolInfo, nullptr, &descriptorPool); + + CreateDebugImage("tools/light.png", lightImage, device); + CreateDebugImage("tools/camera.png", cameraImage, device); +} + +void DebugPass::CreateDebugImage(const char* filepath, DebugImage &image, vk::Device device) { + vk::CreateBuffer(sizeof(matrixData), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + image.uniformMatrixBuffer, + image.uniformMatrixBufferMemory, + device); + + vk::CreateBuffer(sizeof(UniformMatrixData) * 10, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + image.uniformDebugBuffer, + image.uniformDebugBufferMemory, + device); + + int texWidth, texHeight, texChannels; + stbi_uc* pixels = stbi_load(filepath, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + VkDeviceSize imageSize = static_cast(texWidth * texHeight * 4); + + VkImage stagingImage; + VkDeviceMemory stagingImageMemory; + vk::createImage(static_cast(texWidth), + static_cast(texHeight), + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingImage, + stagingImageMemory, + device); + + void* data; + vkMapMemory(device.logicalDevice, stagingImageMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device.logicalDevice, stagingImageMemory); + + stbi_image_free(pixels); + + vk::createImage(static_cast(texWidth), + static_cast(texHeight), + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + image.image, + image.memory, + device); + + vk::transitionImageLayout(stagingImage, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, device); + vk::transitionImageLayout(image.image, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, device); + vk::copyImage(stagingImage, image.image, static_cast(texWidth), static_cast(texHeight), device); + + vk::transitionImageLayout(image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, device); + + vkFreeMemory(device.logicalDevice, stagingImageMemory, nullptr); + vkDestroyImage(device.logicalDevice, stagingImage, nullptr); + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image.image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(device.logicalDevice, &viewInfo, nullptr, &image.view); + + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + vkCreateSampler(device.logicalDevice, &samplerInfo, nullptr, &image.sampler); + + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &descriptorSetLayout; + + vkAllocateDescriptorSets(device.logicalDevice, &allocInfo, &image.descriptorSet); + + VkDescriptorBufferInfo matrixBufferInfo = {}; + matrixBufferInfo.buffer = image.uniformMatrixBuffer; + matrixBufferInfo.offset = 0; + matrixBufferInfo.range = sizeof(UniformMatrixData); + + VkDescriptorBufferInfo debugBufferInfo = {}; + debugBufferInfo.buffer = image.uniformDebugBuffer; + debugBufferInfo.offset = 0; + debugBufferInfo.range = sizeof(UniformDebugData); + + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = image.view; + imageInfo.sampler = image.sampler; + + VkWriteDescriptorSet descriptorWrite; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.pNext = nullptr; + descriptorWrite.dstSet = image.descriptorSet; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &matrixBufferInfo; + + VkWriteDescriptorSet debugDescriptorWrite; + debugDescriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + debugDescriptorWrite.pNext = nullptr; + debugDescriptorWrite.dstSet = image.descriptorSet; + debugDescriptorWrite.dstBinding = 1; + debugDescriptorWrite.dstArrayElement = 0; + debugDescriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + debugDescriptorWrite.descriptorCount = 1; + debugDescriptorWrite.pBufferInfo = &debugBufferInfo; + + VkWriteDescriptorSet imageDescriptorWrite; + imageDescriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + imageDescriptorWrite.pNext = nullptr; + imageDescriptorWrite.dstSet = image.descriptorSet; + imageDescriptorWrite.dstBinding = 2; + imageDescriptorWrite.dstArrayElement = 0; + imageDescriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + imageDescriptorWrite.descriptorCount = 1; + imageDescriptorWrite.pImageInfo = &imageInfo; + + VkWriteDescriptorSet descriptorWrites[] = { descriptorWrite, debugDescriptorWrite, imageDescriptorWrite }; + + vkUpdateDescriptorSets(device.logicalDevice, 3, descriptorWrites, 0, nullptr); +} + +void DebugPass::DestroyDebugImage(DebugImage &image, vk::Device c) { + vkDestroySampler(c.logicalDevice, image.sampler, nullptr); + vkDestroyImageView(c.logicalDevice, image.view, nullptr); + vkDestroyImage(c.logicalDevice, image.image, nullptr); + vkFreeMemory(c.logicalDevice, image.memory, nullptr); + + vkFreeMemory(c.logicalDevice, image.uniformMatrixBufferMemory, nullptr); + vkFreeMemory(c.logicalDevice, image.uniformDebugBufferMemory, nullptr); + vkDestroyBuffer(c.logicalDevice, image.uniformMatrixBuffer, nullptr); + vkDestroyBuffer(c.logicalDevice, image.uniformDebugBuffer, nullptr); +} + +void DebugPass::UploadUniformData(int index, + glm::vec3 position, + glm::vec3 color, + Camera* camera, + VkDeviceMemory matrixMemory, + VkDeviceMemory debugMemory, + uint32_t offsets[2], + vk::Device c) { + int minUniformBufferOffsetAlignment = vk::GetUniformOffsetAlignment(c.physicalDevice); + + offsets[0] = index * Utility::Align(sizeof(UniformMatrixData), minUniformBufferOffsetAlignment); + offsets[1] = index * Utility::Align(sizeof(UniformDebugData), minUniformBufferOffsetAlignment); + + UniformMatrixData uniformData; + uniformData.proj = camera->GetProjection(); + uniformData.proj[1][1] *= -1.0f; + uniformData.view = camera->GetView(); + + glm::vec3 look = glm::normalize(camera->GetEntity()->GetTransform()->GetWorldPosition() - position); + glm::vec3 right = glm::cross(camera->GetEntity()->GetTransform()->GetUp(), look); + glm::vec3 up2 = glm::cross(look, right); + glm::mat4 transform; + transform[0] = glm::vec4(right, 0); + transform[1] = glm::vec4(up2, 0); + transform[2] = glm::vec4(look, 0); + + uniformData.model = glm::translate(glm::mat4(1.0f), position) * transform * glm::scale(glm::mat4(1.0f), glm::vec3(2)); + uniformData.color = glm::vec4(color, 1.0f); + + UniformDebugData debugData; + + void* data; + vkMapMemory(c.logicalDevice, matrixMemory, offsets[0], sizeof(UniformMatrixData), 0, &data); + memcpy(data, &uniformData, sizeof(UniformMatrixData)); + vkUnmapMemory(c.logicalDevice, matrixMemory); + + data = nullptr; + vkMapMemory(c.logicalDevice, debugMemory, offsets[1], sizeof(UniformDebugData), 0, &data); + memcpy(data, &debugData, sizeof(UniformDebugData)); + vkUnmapMemory(c.logicalDevice, debugMemory); +} + +void DebugPass::Record(Camera* camera, VkCommandBuffer commandBuffer, vk::Device device) { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + auto lights = GetEntPool().GetOf(); + for(unsigned int i = 0; i < lights.size(); i++) { + uint32_t offsets[2]; + UploadUniformData(i, + lights[i]->GetEntity()->GetTransform()->GetWorldPosition(), + lights[i]->color, + camera, + lightImage.uniformMatrixBufferMemory, + lightImage.uniformDebugBufferMemory, + offsets, + device); + + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, + 0, + 1, + &lightImage.descriptorSet, + sizeof(offsets) / sizeof(offsets[0]), + offsets); + + //quad + Primitives::DrawQuad(commandBuffer); + } + + auto cameras = GetEntPool().GetOf(); + for(unsigned int i = 0; i < cameras.size(); i++) + { + if(cameras[i]->GetEntity()->tag != "editor") + { + uint32_t offsets[2]; + UploadUniformData(i, + cameras[i]->GetEntity()->GetTransform()->GetWorldPosition(), + glm::vec3(1, 1, 1), + camera, + cameraImage.uniformMatrixBufferMemory, + cameraImage.uniformDebugBufferMemory, + offsets, + device); + + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, + 0, + 1, + &cameraImage.descriptorSet, + sizeof(offsets) / sizeof(offsets[0]), + offsets); + + Primitives::DrawQuad(commandBuffer); + } + } +} + +void DebugPass::Cleanup(vk::Device device) { + DestroyDebugImage(lightImage, device); + DestroyDebugImage(cameraImage, device); + + vkDestroyDescriptorPool(device.logicalDevice, descriptorPool, nullptr); + + vkDestroyShaderModule(device.logicalDevice, vertexShaderModule, nullptr); + vkDestroyShaderModule(device.logicalDevice, fragmentShaderModule, nullptr); + + vkDestroyPipelineLayout(device.logicalDevice, pipelineLayout, nullptr); + vkDestroyPipeline(device.logicalDevice, pipeline, nullptr); + + vkDestroyDescriptorSetLayout(device.logicalDevice, descriptorSetLayout, nullptr); +} diff --git a/engine/renderer/src/deferredpipeline.cpp b/engine/renderer/src/deferredpipeline.cpp new file mode 100644 index 0000000..236301c --- /dev/null +++ b/engine/renderer/src/deferredpipeline.cpp @@ -0,0 +1,1483 @@ +#include "deferredpipeline.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DeferredPipeline::DeferredPipeline(Renderer* renderer) +{ + m_renderer = renderer; + + vk::CreateBuffer(sizeof(UniformObjectData) * ResourceLimits::maxDynamicGeometry, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + m_uniformObjectBuffer, + m_uniformObjectBufferMemory, + m_renderer->device); + + vk::CreateBuffer(sizeof(UniformLightData) * ResourceLimits::maxLights, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + m_uniformLightBuffer, + m_uniformLightBufferMemory, + m_renderer->device); + + vk::CreateBuffer(sizeof(UniformPostData), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + m_uniformPostBuffer, + m_uniformPostBufferMemory, + m_renderer->device); + + PrepareGBuffer(); + PrepareLighting(); + PreparePresentation(); + SetupDescriptorLayout(); + PreparePipelines(); + SetupDescriptorPool(); + SetupDescriptorSets(); + + //swapchain framebuffers + m_swapchainFramebuffers.resize(m_renderer->swapchainImageViews.size()); + for(unsigned int i = 0; i < m_renderer->swapchainImageViews.size(); i++) + { + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = m_renderPasses.presentation; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = &m_renderer->swapchainImageViews[i]; + framebufferInfo.width = m_renderer->swapchainExtent.width; + framebufferInfo.height = m_renderer->swapchainExtent.height; + framebufferInfo.layers = 1; + + vkCreateFramebuffer(m_renderer->device.logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); + } + + VkCommandBufferAllocateInfo bufferAllocInfo = {}; + bufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + bufferAllocInfo.commandPool = m_renderer->device.commandPool; + bufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + bufferAllocInfo.commandBufferCount = 2; + + vkAllocateCommandBuffers(m_renderer->device.logicalDevice, &bufferAllocInfo, &m_gBufferCommandBuffer); + vkAllocateCommandBuffers(m_renderer->device.logicalDevice, &bufferAllocInfo, &m_lightingCommandBuffer); + + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + vkCreateSemaphore(m_renderer->device.logicalDevice, &semaphoreInfo, nullptr, &m_renderFinishedSemaphore); + vkCreateSemaphore(m_renderer->device.logicalDevice, &semaphoreInfo, nullptr, &m_gbufferFinishedSemaphore); + vkCreateSemaphore(m_renderer->device.logicalDevice, &semaphoreInfo, nullptr, &m_lightingFinishedSemaphore); + + DrawList::onStaticGeometryUpdate = [=]() + { + for(auto command : m_staticDrawCommands) + { + vkFreeMemory(m_renderer->device.logicalDevice, command.indirectStateBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.vertexBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.indexBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.modelBufferMemory, nullptr); + + vkDestroyBuffer(m_renderer->device.logicalDevice, command.indirectStateBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.vertexBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.indexBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.modelBuffer, nullptr); + + m_staticDrawCommands.pop_back(); + } + m_staticDrawCommands.clear(); + + auto staticGeometry = m_renderer->GetDrawList().GetStaticGeometry(); + for(auto& geometry : staticGeometry.drawCommands) + { + StaticDrawCommand drawCommand; + drawCommand.material = geometry.first; + drawCommand.numGeometry = static_cast(geometry.second.geometryData.size()); + + std::vector indirectCmds; + + for(auto& cmd : geometry.second.geometryData) + { + VkDrawIndexedIndirectCommand indirectCommand = {}; + indirectCommand.instanceCount = 1; + indirectCommand.firstInstance = static_cast(cmd.baseInstance); + indirectCommand.firstIndex = static_cast(cmd.firstVertex); + indirectCommand.indexCount = static_cast(cmd.vertexCount); + indirectCommand.vertexOffset = cmd.baseVertex; + + indirectCmds.push_back(indirectCommand); + } + + VkBuffer indirectStagingBuffer; + VkDeviceMemory indirectStagingBufferMemory; + + VkDeviceSize indirectSize = sizeof(VkDrawIndexedIndirectCommand) * indirectCmds.size(); + vk::CreateBuffer(indirectSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indirectStagingBuffer, + indirectStagingBufferMemory, + m_renderer->device); + + void* data; + vkMapMemory(m_renderer->device.logicalDevice, indirectStagingBufferMemory, 0, indirectSize, 0, &data); + memcpy(data, indirectCmds.data(), static_cast(indirectSize)); + vkUnmapMemory(m_renderer->device.logicalDevice, indirectStagingBufferMemory); + + vk::CreateBuffer(indirectSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + drawCommand.indirectStateBuffer, + drawCommand.indirectStateBufferMemory, + m_renderer->device); + vk::CopyBuffer(indirectStagingBuffer, drawCommand.indirectStateBuffer, indirectSize, m_renderer->device); + + vkFreeMemory(m_renderer->device.logicalDevice, indirectStagingBufferMemory, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, indirectStagingBuffer, nullptr); + + //VERTICES + VkDeviceMemory vertexStagingBufferMemory; + VkBuffer vertexStagingBuffer; + + VkDeviceSize vertexSize = sizeof(Vertex) * geometry.second.vertices.size(); + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexStagingBuffer, + vertexStagingBufferMemory, + m_renderer->device); + + data = nullptr; + vkMapMemory(m_renderer->device.logicalDevice, vertexStagingBufferMemory, 0, vertexSize, 0, &data); + memcpy(data, geometry.second.vertices.data(), static_cast(vertexSize)); + vkUnmapMemory(m_renderer->device.logicalDevice, vertexStagingBufferMemory); + + //INDICES + VkDeviceMemory indexStagingBufferMemory; + VkBuffer indexStagingBuffer; + + VkDeviceSize indexSize = sizeof(unsigned int) * geometry.second.indices.size(); + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indexStagingBuffer, + indexStagingBufferMemory, + m_renderer->device); + + data = nullptr; + vkMapMemory(m_renderer->device.logicalDevice, indexStagingBufferMemory, 0, indexSize, 0, &data); + memcpy(data, geometry.second.indices.data(), static_cast(indexSize)); + vkUnmapMemory(m_renderer->device.logicalDevice, indexStagingBufferMemory); + + //MODELS + VkDeviceMemory modelStagingBufferMemory; + VkBuffer modelStagingBuffer; + + VkDeviceSize modelSize = sizeof(glm::mat4) * geometry.second.models.size(); + vk::CreateBuffer(modelSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + modelStagingBuffer, + modelStagingBufferMemory, + m_renderer->device); + + data = nullptr; + vkMapMemory(m_renderer->device.logicalDevice, modelStagingBufferMemory, 0, modelSize, 0, &data); + memcpy(data, geometry.second.models.data(), static_cast(modelSize)); + vkUnmapMemory(m_renderer->device.logicalDevice, modelStagingBufferMemory); + + //buffer upload + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + drawCommand.vertexBuffer, + drawCommand.vertexBufferMemory, + m_renderer->device); + vk::CopyBuffer(vertexStagingBuffer, drawCommand.vertexBuffer, vertexSize, m_renderer->device); + + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + drawCommand.indexBuffer, + drawCommand.indexBufferMemory, + m_renderer->device); + vk::CopyBuffer(indexStagingBuffer, drawCommand.indexBuffer, indexSize, m_renderer->device); + + vk::CreateBuffer(modelSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + drawCommand.modelBuffer, + drawCommand.modelBufferMemory, + m_renderer->device); + vk::CopyBuffer(modelStagingBuffer, drawCommand.modelBuffer, modelSize, m_renderer->device); + + vkFreeMemory(m_renderer->device.logicalDevice, vertexStagingBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, indexStagingBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, modelStagingBufferMemory, nullptr); + + vkDestroyBuffer(m_renderer->device.logicalDevice, vertexStagingBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, indexStagingBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, modelStagingBuffer, nullptr); + + m_staticDrawCommands.push_back(drawCommand); + } + }; + + //placeholder image + { + char* pixels = new char[1]; + pixels[0] = 0; + + VkImage stagingImage; + VkDeviceMemory stagingImageMemory; + vk::createImage(1, + 1, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingImage, + stagingImageMemory, + m_renderer->device); + + void* data; + vkMapMemory(m_renderer->device.logicalDevice, stagingImageMemory, 0, 1, 0, &data); + memcpy(data, pixels, 1); + vkUnmapMemory(m_renderer->device.logicalDevice, stagingImageMemory); + + vk::createImage(1, + 1, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + m_placeholderImage.image, + m_placeholderImage.memory, + m_renderer->device); + + vk::transitionImageLayout(stagingImage, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_renderer->device); + vk::transitionImageLayout(m_placeholderImage.image, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_renderer->device); + vk::copyImage(stagingImage, m_placeholderImage.image, 1, 1, m_renderer->device); + + vk::transitionImageLayout(m_placeholderImage.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, m_renderer->device); + + vkFreeMemory(m_renderer->device.logicalDevice, stagingImageMemory, nullptr); + vkDestroyImage(m_renderer->device.logicalDevice, stagingImage, nullptr); + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = m_placeholderImage.image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(m_renderer->device.logicalDevice, &viewInfo, nullptr, &m_placeholderImage.view); + + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + vkCreateSampler(m_renderer->device.logicalDevice, &samplerInfo, nullptr, &m_placeholderImage.sampler); + } +} + +DeferredPipeline::~DeferredPipeline() +{ + //placeholder image + vkDestroySampler(m_renderer->device.logicalDevice, m_placeholderImage.sampler, nullptr); + vkDestroyImageView(m_renderer->device.logicalDevice, m_placeholderImage.view, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, m_placeholderImage.memory, nullptr); + vkDestroyImage(m_renderer->device.logicalDevice, m_placeholderImage.image, nullptr); + + ClearStaticGeometryBuffers(); + + for(unsigned int i = 0; i < m_renderer->swapchainImageViews.size(); i++) + vkDestroyFramebuffer(m_renderer->device.logicalDevice, m_swapchainFramebuffers[i], nullptr); + + vkDestroySemaphore(m_renderer->device.logicalDevice, m_gbufferFinishedSemaphore, nullptr); + vkDestroySemaphore(m_renderer->device.logicalDevice, m_lightingFinishedSemaphore, nullptr); + vkDestroySemaphore(m_renderer->device.logicalDevice, m_renderFinishedSemaphore, nullptr); + + vkDestroyDescriptorPool(m_renderer->device.logicalDevice, m_descriptorPool, nullptr); + + vkDestroyPipeline(m_renderer->device.logicalDevice, m_pipelines.geometryDynamic, nullptr); + vkDestroyPipeline(m_renderer->device.logicalDevice, m_pipelines.geometryStatic, nullptr); + vkDestroyPipeline(m_renderer->device.logicalDevice, m_pipelines.pointLighting, nullptr); + vkDestroyPipeline(m_renderer->device.logicalDevice, m_pipelines.postProcessing, nullptr); + vkDestroyPipeline(m_renderer->device.logicalDevice, m_pipelines.ambientLighting, nullptr); + + vkDestroyPipelineLayout(m_renderer->device.logicalDevice, m_pipelineLayouts.pointLighting, nullptr); + vkDestroyPipelineLayout(m_renderer->device.logicalDevice, m_pipelineLayouts.ambientLighting, nullptr); + vkDestroyPipelineLayout(m_renderer->device.logicalDevice, m_pipelineLayouts.gBuffer, nullptr); + vkDestroyPipelineLayout(m_renderer->device.logicalDevice, m_pipelineLayouts.postProcessing, nullptr); + + vkDestroyDescriptorSetLayout(m_renderer->device.logicalDevice, m_descriptorLayouts.gBuffer, nullptr); + vkDestroyDescriptorSetLayout(m_renderer->device.logicalDevice, m_descriptorLayouts.ambientLighting, nullptr); + vkDestroyDescriptorSetLayout(m_renderer->device.logicalDevice, m_descriptorLayouts.pointLighting, nullptr); + vkDestroyDescriptorSetLayout(m_renderer->device.logicalDevice, m_descriptorLayouts.postProcessing, nullptr); + + vkFreeMemory(m_renderer->device.logicalDevice, m_uniformObjectBufferMemory, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, m_uniformObjectBuffer, nullptr); + + vkFreeMemory(m_renderer->device.logicalDevice, m_uniformLightBufferMemory, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, m_uniformLightBuffer, nullptr); + + vkFreeMemory(m_renderer->device.logicalDevice, m_uniformPostBufferMemory, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, m_uniformPostBuffer, nullptr); + + //final result + vkDestroySampler(m_renderer->device.logicalDevice, m_finalResult.colorSampler, nullptr); + vkDestroyFramebuffer(m_renderer->device.logicalDevice, m_finalResult.framebuffer, nullptr); + DestroyAttachment(m_finalResult.color); + + //gbuffer + vkDestroySampler(m_renderer->device.logicalDevice, m_gBuffer.colorSampler, nullptr); + vkDestroyFramebuffer(m_renderer->device.logicalDevice, m_gBuffer.framebuffer, nullptr); + DestroyAttachment(m_gBuffer.depth); + DestroyAttachment(m_gBuffer.material); + DestroyAttachment(m_gBuffer.albedo); + DestroyAttachment(m_gBuffer.normal); + DestroyAttachment(m_gBuffer.position); + + vkDestroyRenderPass(m_renderer->device.logicalDevice, m_renderPasses.lighting, nullptr); + vkDestroyRenderPass(m_renderer->device.logicalDevice, m_renderPasses.gBuffer, nullptr); + vkDestroyRenderPass(m_renderer->device.logicalDevice, m_renderPasses.presentation, nullptr); +} + +void DeferredPipeline::RecordGBuffer() +{ + m_renderer->GetDrawList().RebuildDynamicLists(); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + + vkBeginCommandBuffer(m_gBufferCommandBuffer, &beginInfo); + + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = m_renderPasses.gBuffer; + renderPassInfo.framebuffer = m_gBuffer.framebuffer; + renderPassInfo.renderArea.extent = m_renderer->swapchainExtent; + + std::array clearValues; + clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f }; + clearValues[1].color = { 0.0f, 0.0f, 0.0f, 1.0f }; + clearValues[2].color = { 0.0f, 0.0f, 0.0f, 1.0f }; + clearValues[3].color = { 0.0f, 0.0f, 0.0f, 1.0f }; + clearValues[4].depthStencil = { 1.0f, 0 }; + + renderPassInfo.clearValueCount = static_cast(clearValues.size()); + renderPassInfo.pClearValues = clearValues.data(); + + vkCmdBeginRenderPass(m_gBufferCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vk::initializers::Viewport(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height); + vkCmdSetViewport(m_gBufferCommandBuffer, 0, 1, &viewport); + + VkRect2D scissor = vk::initializers::Rect2D(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height, 0, 0); + vkCmdSetScissor(m_gBufferCommandBuffer, 0, 1, &scissor); + + vkCmdBindPipeline(m_gBufferCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines.geometryStatic); + + auto cameras = GetEntPool().GetOf(); + if(cameras.size() != 0) + { + Camera* camera = cameras[0]; + + uint32_t minUniformBufferOffsetAlignment = vk::GetUniformOffsetAlignment(m_renderer->device.physicalDevice); + uint32_t i = 0; + + //draw static geometry first + for(auto& geometry : m_staticDrawCommands) + { + if(!m_samplerSets.count(geometry.material) || geometry.material->invalidated) + CreateSet(geometry.material); + + VkDescriptorSet set = m_samplerSets.at(geometry.material); + + uint32_t objectOffset = Utility::Align(sizeof(UniformObjectData) * i, minUniformBufferOffsetAlignment); + + UniformObjectData objectData; + objectData.projection = camera->GetProjection(); + objectData.projection[1][1] *= -1.0f; + objectData.view = camera->GetView(); + objectData.roughness = geometry.material->roughness; + objectData.metallic = geometry.material->metallic; + objectData.albedoTint = glm::vec4(geometry.material->albedoColor, 0.0); + objectData.emission = glm::vec4(geometry.material->emissionColor, 0.0); + + void * data = nullptr; + vkMapMemory(m_renderer->device.logicalDevice, m_uniformObjectBufferMemory, objectOffset, sizeof(UniformObjectData), 0, &data); + memcpy(data, &objectData, sizeof(UniformObjectData)); + vkUnmapMemory(m_renderer->device.logicalDevice, m_uniformObjectBufferMemory); + + VkDeviceSize offset[] = {0}; + + vkCmdBindDescriptorSets(m_gBufferCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayouts.gBuffer, 0, 1, &set, 1, &objectOffset); + + vkCmdBindVertexBuffers(m_gBufferCommandBuffer, 0, 1, &geometry.vertexBuffer, offset); + vkCmdBindVertexBuffers(m_gBufferCommandBuffer, 1, 1, &geometry.modelBuffer, offset); + vkCmdBindIndexBuffer(m_gBufferCommandBuffer, geometry.indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexedIndirect(m_gBufferCommandBuffer, geometry.indirectStateBuffer, 0, geometry.numGeometry, sizeof(VkDrawIndexedIndirectCommand)); + i++; + } + + vkCmdBindPipeline(m_gBufferCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines.geometryDynamic); + + i = 0; + + //and then draw dynamic geometry + auto dynamicGeometry = m_renderer->GetDrawList().GetDynamicGeometry(); + for(auto& geometry : dynamicGeometry) + { + if(!m_samplerSets.count(geometry->material) || geometry->material->invalidated) + CreateSet(geometry->material); + + VkDescriptorSet set = m_samplerSets.at(geometry->material); + + uint32_t objectOffset = Utility::Align(sizeof(UniformObjectData) * i, minUniformBufferOffsetAlignment); + + UniformObjectData objectData; + objectData.projection = camera->GetProjection(); + objectData.projection[1][1] *= -1.0f; + objectData.view = camera->GetView(); + objectData.model = geometry->model; + objectData.roughness = geometry->material->roughness; + objectData.metallic = geometry->material->metallic; + objectData.albedoTint = glm::vec4(geometry->material->albedoColor, 0.0); + objectData.emission = glm::vec4(geometry->material->emissionColor, 0.0); + + void* data = nullptr; + vkMapMemory(m_renderer->device.logicalDevice, m_uniformObjectBufferMemory, objectOffset, sizeof(UniformObjectData), 0, &data); + memcpy(data, &objectData, sizeof(UniformObjectData)); + vkUnmapMemory(m_renderer->device.logicalDevice, m_uniformObjectBufferMemory); + + VkDeviceSize offset[] = { 0 }; + + vkCmdBindDescriptorSets(m_gBufferCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayouts.gBuffer, 0, 1, &set, 1, &objectOffset); + + VulkanMesh vulkanMesh = m_renderer->GetMesh(geometry->mesh); + + vkCmdBindVertexBuffers(m_gBufferCommandBuffer, 0, 1, &vulkanMesh.vertexBuffer, offset); + vkCmdBindIndexBuffer(m_gBufferCommandBuffer, vulkanMesh.indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(m_gBufferCommandBuffer, static_cast(geometry->mesh->GetIndices().size()), 1, 0, 0, 0); + i++; + } + } + + vkCmdEndRenderPass(m_gBufferCommandBuffer); + vkEndCommandBuffer(m_gBufferCommandBuffer); +} + +void DeferredPipeline::RecordLighting() +{ + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + + vkBeginCommandBuffer(m_lightingCommandBuffer, &beginInfo); + + VkClearValue clearValue = {}; + clearValue.color = { 0.0f, 0.0f, 0.0f, 1.0f }; + + VkRenderPassBeginInfo postRenderPassInfo = {}; + postRenderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + postRenderPassInfo.renderPass = m_renderPasses.lighting; + postRenderPassInfo.framebuffer = m_finalResult.framebuffer; + postRenderPassInfo.renderArea.offset = { 0, 0 }; + postRenderPassInfo.renderArea.extent = m_renderer->swapchainExtent; + postRenderPassInfo.clearValueCount = 1; + postRenderPassInfo.pClearValues = &clearValue; + + vkCmdBeginRenderPass(m_lightingCommandBuffer, &postRenderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vk::initializers::Viewport(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height); + vkCmdSetViewport(m_lightingCommandBuffer, 0, 1, &viewport); + + VkRect2D scissor = vk::initializers::Rect2D(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height, 0, 0); + vkCmdSetScissor(m_lightingCommandBuffer, 0, 1, &scissor); + + auto cameras = GetEntPool().GetOf(); + if(cameras.size() != 0) + { + Camera *camera = cameras[0]; + + vkCmdBindPipeline(m_lightingCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines.ambientLighting); + vkCmdBindDescriptorSets(m_lightingCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayouts.ambientLighting, 0, 1, &m_ambientDescriptorSet, 0, 0); + + Primitives::DrawQuad(m_lightingCommandBuffer); + + int minUniformBufferOffsetAlignment = vk::GetUniformOffsetAlignment(m_renderer->device.physicalDevice); + + uint32_t i = 0; + + auto lights = GetEntPool().GetOf(); + for(auto light : lights) + { + + UniformLightData lightData; + lightData.projection = camera->GetProjection(); + lightData.projection[1][1] *= -1.0f; + lightData.view = camera->GetView(); + lightData.model = + glm::translate(glm::mat4(1.0f), light->GetEntity()->GetTransform()->position) * + glm::scale(glm::mat4(1.0f), glm::vec3(light->radius)); + lightData.lightPosition = glm::vec4(light->GetEntity()->GetTransform()->position, 1.0); + lightData.radius = light->radius; + lightData.cameraPosition = glm::vec4(camera->GetEntity()->GetTransform()->GetWorldPosition(), 1.0); + lightData.color = light->color; + lightData.viewport.x = m_renderer->swapchainExtent.width; + lightData.viewport.y = m_renderer->swapchainExtent.height; + + uint32_t offset = i * Utility::Align(sizeof(UniformLightData), minUniformBufferOffsetAlignment); + + void *data; + vkMapMemory(m_renderer->device.logicalDevice, m_uniformLightBufferMemory, offset, sizeof(UniformLightData), 0, &data); + memcpy(data, &lightData, sizeof(UniformLightData)); + vkUnmapMemory(m_renderer->device.logicalDevice, m_uniformLightBufferMemory); + + vkCmdBindPipeline(m_lightingCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines.pointLighting); + vkCmdBindDescriptorSets(m_lightingCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayouts.pointLighting, 0, 1, &m_lightingDescriptorSet, 1, &offset); + + Primitives::DrawSphere(m_lightingCommandBuffer); + i++; + } + } + + vkCmdEndRenderPass(m_lightingCommandBuffer); + vkEndCommandBuffer(m_lightingCommandBuffer); +} + +void DeferredPipeline::Record(VkCommandBuffer commandBuffer, uint32_t index) +{ + //first frame + if(index == 0) + { + RecordGBuffer(); + RecordLighting(); + } + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkClearValue clearValue = {}; + clearValue.color = { 0.0f, 0.0f, 0.0f, 1.0f }; + + VkRenderPassBeginInfo postRenderPassInfo = {}; + postRenderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + postRenderPassInfo.renderPass = m_renderPasses.presentation; + postRenderPassInfo.framebuffer = m_swapchainFramebuffers[index]; + postRenderPassInfo.renderArea.offset = { 0, 0 }; + postRenderPassInfo.renderArea.extent = m_renderer->swapchainExtent; + postRenderPassInfo.clearValueCount = 1; + postRenderPassInfo.pClearValues = &clearValue; + + vkCmdBeginRenderPass(commandBuffer, &postRenderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vk::initializers::Viewport(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height); + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor = vk::initializers::Rect2D(m_renderer->swapchainExtent.width, m_renderer->swapchainExtent.height, 0, 0); + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + UniformPostData postData; + + void *data; + vkMapMemory(m_renderer->device.logicalDevice, m_uniformPostBufferMemory, 0, sizeof(UniformPostData), 0, &data); + memcpy(data, &postData, sizeof(UniformPostData)); + vkUnmapMemory(m_renderer->device.logicalDevice, m_uniformPostBufferMemory); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelines.postProcessing); + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipelineLayouts.postProcessing, + 0, + 1, + &m_postDescriptorSet, + 0, + 0); + + Primitives::DrawQuad(commandBuffer); + + if(m_renderer->onRenderPassEnd) + m_renderer->onRenderPassEnd(commandBuffer); + + vkCmdEndRenderPass(commandBuffer); + vkEndCommandBuffer(commandBuffer); +} + +void DeferredPipeline::Submit(SubmitInfo si) +{ + VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.signalSemaphoreCount = 1; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitDstStageMask = &waitStage; + + submitInfo.pCommandBuffers = &m_gBufferCommandBuffer; + submitInfo.pWaitSemaphores = &si.ias; + submitInfo.pSignalSemaphores = &m_gbufferFinishedSemaphore; + + vkQueueSubmit(m_renderer->device.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + + submitInfo.pCommandBuffers = &m_lightingCommandBuffer; + submitInfo.pWaitSemaphores = &m_gbufferFinishedSemaphore; + submitInfo.pSignalSemaphores = &m_lightingFinishedSemaphore; + + vkQueueSubmit(m_renderer->device.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + + submitInfo.pCommandBuffers = &si.cmdBuf; + submitInfo.pWaitSemaphores = &m_lightingFinishedSemaphore; + submitInfo.pSignalSemaphores = &m_renderFinishedSemaphore; + + vkQueueSubmit(m_renderer->device.graphicsQueue, 1, &submitInfo, si.submitFence); +} + +void DeferredPipeline::PrepareGBuffer() +{ + CreateAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, m_gBuffer.position); //position + CreateAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, m_gBuffer.normal); //normals + CreateAttachment(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, m_gBuffer.albedo); //albedo + CreateAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, m_gBuffer.material); //material + CreateAttachment(VK_FORMAT_D32_SFLOAT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, m_gBuffer.depth); //depth + + std::array attachmentDescriptions = { + vk::initializers::TypicalAttachment(m_gBuffer.position.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), //postion + vk::initializers::TypicalAttachment(m_gBuffer.normal.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), //normals + vk::initializers::TypicalAttachment(m_gBuffer.albedo.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), //albedo + vk::initializers::TypicalAttachment(m_gBuffer.material.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL), //albedo + vk::initializers::TypicalAttachment(m_gBuffer.depth.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL), //depth + }; + + std::array colorAttachmentRefs = { + vk::initializers::TypicalColorReference(0), + vk::initializers::TypicalColorReference(1), + vk::initializers::TypicalColorReference(2), + vk::initializers::TypicalColorReference(3) + }; + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 4; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subPass = {}; + subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subPass.pColorAttachments = colorAttachmentRefs.data(); + subPass.colorAttachmentCount = static_cast(colorAttachmentRefs.size()); + subPass.pDepthStencilAttachment = &depthReference; + + std::array dependencies = {}; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = static_cast(attachmentDescriptions.size()); + renderPassCreateInfo.pAttachments = attachmentDescriptions.data(); + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subPass; + renderPassCreateInfo.dependencyCount = static_cast(dependencies.size()); + renderPassCreateInfo.pDependencies = dependencies.data(); + + vkCreateRenderPass(m_renderer->device.logicalDevice, &renderPassCreateInfo, nullptr, &m_renderPasses.gBuffer); + + std::array framebufferAttachments = { + m_gBuffer.position.view, + m_gBuffer.normal.view, + m_gBuffer.albedo.view, + m_gBuffer.material.view, + m_gBuffer.depth.view + }; + + VkFramebufferCreateInfo framebufferCreateInfo = {}; + framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCreateInfo.renderPass = m_renderPasses.gBuffer; + framebufferCreateInfo.attachmentCount = static_cast(framebufferAttachments.size()); + framebufferCreateInfo.pAttachments = framebufferAttachments.data(); + framebufferCreateInfo.width = m_renderer->swapchainExtent.width; + framebufferCreateInfo.height = m_renderer->swapchainExtent.height; + framebufferCreateInfo.layers = 1; + + vkCreateFramebuffer(m_renderer->device.logicalDevice, &framebufferCreateInfo, nullptr, &m_gBuffer.framebuffer); + + VkSamplerCreateInfo samplerCreateInfo = {}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_NEAREST; + samplerCreateInfo.minFilter = VK_FILTER_NEAREST; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; + samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.maxAnisotropy = 0; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 1.0f; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + + vkCreateSampler(m_renderer->device.logicalDevice, &samplerCreateInfo, nullptr, &m_gBuffer.colorSampler); +} + +void DeferredPipeline::PrepareLighting() +{ + CreateAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, m_finalResult.color); //color + + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = m_finalResult.color.format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subPass = {}; + subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subPass.colorAttachmentCount = 1; + subPass.pColorAttachments = &colorAttachmentRef; + + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = 1; + renderPassCreateInfo.pAttachments = &colorAttachment; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subPass; + renderPassCreateInfo.dependencyCount = 2; + renderPassCreateInfo.pDependencies = dependencies.data(); + + vkCreateRenderPass(m_renderer->device.logicalDevice, &renderPassCreateInfo, nullptr, &m_renderPasses.lighting); + + VkFramebufferCreateInfo framebufferCreateInfo = {}; + framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCreateInfo.renderPass = m_renderPasses.lighting; + framebufferCreateInfo.attachmentCount = 1; + framebufferCreateInfo.pAttachments = &m_finalResult.color.view; + framebufferCreateInfo.width = m_renderer->swapchainExtent.width; + framebufferCreateInfo.height = m_renderer->swapchainExtent.height; + framebufferCreateInfo.layers = 1; + + vkCreateFramebuffer(m_renderer->device.logicalDevice, &framebufferCreateInfo, nullptr, &m_finalResult.framebuffer); + + VkSamplerCreateInfo samplerCreateInfo = {}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_NEAREST; + samplerCreateInfo.minFilter = VK_FILTER_NEAREST; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; + samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.maxAnisotropy = 0; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 1.0f; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + + vkCreateSampler(m_renderer->device.logicalDevice, &samplerCreateInfo, nullptr, &m_finalResult.colorSampler); +} + +void DeferredPipeline::PreparePresentation() +{ + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = m_renderer->swapchainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subPass = {}; + subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subPass.colorAttachmentCount = 1; + subPass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = 1; + renderPassCreateInfo.pAttachments = &colorAttachment; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subPass; + + vkCreateRenderPass(m_renderer->device.logicalDevice, &renderPassCreateInfo, nullptr, &m_renderPasses.presentation); +} + +void DeferredPipeline::SetupDescriptorLayout() +{ + //gbuffer + { + std::array bindings = { + vk::initializers::DescriptorBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT), //object data + vk::initializers::DescriptorBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //albedo + vk::initializers::DescriptorBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //normal + vk::initializers::DescriptorBinding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //roughness + vk::initializers::DescriptorBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT) //metallic + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + vkCreateDescriptorSetLayout(m_renderer->device.logicalDevice, &layoutInfo, nullptr, &m_descriptorLayouts.gBuffer); + } + + //ambient lighting + { + std::array bindings = { + vk::initializers::DescriptorBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //albedo + vk::initializers::DescriptorBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //material + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + vkCreateDescriptorSetLayout(m_renderer->device.logicalDevice, &layoutInfo, nullptr, &m_descriptorLayouts.ambientLighting); + } + + //point lighting + { + std::array bindings = { + vk::initializers::DescriptorBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT), //position + vk::initializers::DescriptorBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //position + vk::initializers::DescriptorBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //normals + vk::initializers::DescriptorBinding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //albedo + vk::initializers::DescriptorBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT) //material + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + vkCreateDescriptorSetLayout(m_renderer->device.logicalDevice, &layoutInfo, nullptr, &m_descriptorLayouts.pointLighting); + } + + //postprocessing + { + std::array bindings = { + vk::initializers::DescriptorBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT), //post data + vk::initializers::DescriptorBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //scene texture + vk::initializers::DescriptorBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //bloom level 1 + vk::initializers::DescriptorBinding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //bloom level 2 + vk::initializers::DescriptorBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT), //bloom level 3 + vk::initializers::DescriptorBinding(5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT) //depth + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + vkCreateDescriptorSetLayout(m_renderer->device.logicalDevice, &layoutInfo, nullptr, &m_descriptorLayouts.postProcessing); + } +} + +//TODO: find some way to write this better +void DeferredPipeline::PreparePipelines() +{ + //gbuffer dynamic + { + std::array bindings = { + vk::initializers::BindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX) //vertex + }; + + std::array attributes = { + vk::initializers::VertexAttribute(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)), + vk::initializers::VertexAttribute(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)), + vk::initializers::VertexAttribute(0, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)), + vk::initializers::VertexAttribute(0, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, tangent)) + }; + + m_inputStates.geometryDynamic = {}; + m_inputStates.geometryDynamic.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + m_inputStates.geometryDynamic.vertexBindingDescriptionCount = static_cast(bindings.size()); + m_inputStates.geometryDynamic.pVertexBindingDescriptions = bindings.data(); + m_inputStates.geometryDynamic.vertexAttributeDescriptionCount = static_cast(attributes.size()); + m_inputStates.geometryDynamic.pVertexAttributeDescriptions = attributes.data(); + } + + //gbuffer static + { + std::array bindings = { + vk::initializers::BindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX), //vertex + vk::initializers::BindingDescription(1, sizeof(glm::mat4), VK_VERTEX_INPUT_RATE_INSTANCE), //model + }; + + std::array attributes = { + vk::initializers::VertexAttribute(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)), + vk::initializers::VertexAttribute(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)), + vk::initializers::VertexAttribute(0, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)), + vk::initializers::VertexAttribute(0, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, tangent)), + vk::initializers::VertexAttribute(1, 4, VK_FORMAT_R32G32B32A32_SFLOAT, 0), + vk::initializers::VertexAttribute(1, 5, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(glm::vec4)), + vk::initializers::VertexAttribute(1, 6, VK_FORMAT_R32G32B32A32_SFLOAT, 2 * sizeof(glm::vec4)), + vk::initializers::VertexAttribute(1, 7, VK_FORMAT_R32G32B32A32_SFLOAT, 3 * sizeof(glm::vec4)) + }; + + m_inputStates.geometryStatic = {}; + m_inputStates.geometryStatic.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + m_inputStates.geometryStatic.vertexBindingDescriptionCount = static_cast(bindings.size()); + m_inputStates.geometryStatic.pVertexBindingDescriptions = bindings.data(); + m_inputStates.geometryStatic.vertexAttributeDescriptionCount = static_cast(attributes.size()); + m_inputStates.geometryStatic.pVertexAttributeDescriptions = attributes.data(); + } + + //quad + { + VkVertexInputBindingDescription vertexBinding = vk::initializers::BindingDescription(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX); //vertex + VkVertexInputAttributeDescription vertexAttribute = vk::initializers::VertexAttribute(0, 0, VK_FORMAT_R32G32B32_SFLOAT); + + m_inputStates.quad = {}; + m_inputStates.quad.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + m_inputStates.quad.vertexBindingDescriptionCount = 1; + m_inputStates.quad.pVertexBindingDescriptions = &vertexBinding; + m_inputStates.quad.vertexAttributeDescriptionCount = 1; + m_inputStates.quad.pVertexAttributeDescriptions = &vertexAttribute; + } + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vk::initializers::InputAssemblyState(); + inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + + VkPipelineRasterizationStateCreateInfo rasterizationState = vk::initializers::RasterizationState( + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_COUNTER_CLOCKWISE, + 0 + ); + + VkPipelineColorBlendAttachmentState colorBlendAttachment = vk::initializers::ColorBlendAttachmentState(0xf); + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT; + + VkPipelineColorBlendStateCreateInfo colorBlendState = vk::initializers::ColorBlendState(1, &colorBlendAttachment); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = vk::initializers::DepthStencilState( + VK_TRUE, + VK_FALSE, + VK_COMPARE_OP_LESS_OR_EQUAL + ); + + VkPipelineViewportStateCreateInfo viewportState = vk::initializers::ViewportState(); + + VkPipelineMultisampleStateCreateInfo multisampleState = vk::initializers::MultisampleState(); + + std::array enabledDynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = vk::initializers::DynamicState( + static_cast(enabledDynamicStates.size()), + enabledDynamicStates.data() + ); + + VkShaderModule vertexLightingShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/point.vert", + m_renderer->device.logicalDevice + ); + VkShaderModule fragmentLightingShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/point.frag", + m_renderer->device.logicalDevice + ); + + std::array lightingShaderStages = { + vk::initializers::ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexLightingShaderModule), + vk::initializers::ShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentLightingShaderModule) + }; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_descriptorLayouts.pointLighting; + + vkCreatePipelineLayout(m_renderer->device.logicalDevice, &pipelineLayoutInfo, nullptr, &m_pipelineLayouts.pointLighting); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.stageCount = static_cast(lightingShaderStages.size()); + pipelineCreateInfo.pStages = lightingShaderStages.data(); + pipelineCreateInfo.pVertexInputState = &m_inputStates.quad; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.layout = m_pipelineLayouts.pointLighting; + pipelineCreateInfo.renderPass = m_renderPasses.lighting; + + vkCreateGraphicsPipelines(m_renderer->device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &m_pipelines.pointLighting); + + pipelineLayoutInfo.pSetLayouts = &m_descriptorLayouts.ambientLighting; + vkCreatePipelineLayout(m_renderer->device.logicalDevice, &pipelineLayoutInfo, nullptr, &m_pipelineLayouts.ambientLighting); + + VkShaderModule vertexQuadShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/quad.vert", + m_renderer->device.logicalDevice + ); + + VkShaderModule fragmentAmbientShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/ambient.frag", + m_renderer->device.logicalDevice + ); + + std::array ambientShaderStages = { + vk::initializers::ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexQuadShaderModule), + vk::initializers::ShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentAmbientShaderModule) + }; + + rasterizationState.frontFace = VK_FRONT_FACE_CLOCKWISE; + pipelineCreateInfo.pStages = ambientShaderStages.data(); + pipelineCreateInfo.layout = m_pipelineLayouts.ambientLighting; + inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + vkCreateGraphicsPipelines(m_renderer->device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &m_pipelines.ambientLighting); + + std::array colorBlendAttachments ={ + vk::initializers::ColorBlendAttachmentState(0xf), + vk::initializers::ColorBlendAttachmentState(0xf), + vk::initializers::ColorBlendAttachmentState(0xf), + vk::initializers::ColorBlendAttachmentState(0xf) + }; + colorBlendState = vk::initializers::ColorBlendState(static_cast(colorBlendAttachments.size()), colorBlendAttachments.data()); + + depthStencilState.depthWriteEnable = VK_TRUE; + + pipelineLayoutInfo.pSetLayouts = &m_descriptorLayouts.gBuffer; + vkCreatePipelineLayout(m_renderer->device.logicalDevice, &pipelineLayoutInfo, nullptr, &m_pipelineLayouts.gBuffer); + + VkShaderModule vertexGBufferShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/static.vert", + m_renderer->device.logicalDevice + ); + VkShaderModule fragmentGBufferShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/gbuffer.frag", + m_renderer->device.logicalDevice + ); + + std::array gbufferShaderStages = { + vk::initializers::ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexGBufferShaderModule), + vk::initializers::ShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentGBufferShaderModule) + }; + + pipelineCreateInfo.stageCount = static_cast(gbufferShaderStages.size()); + pipelineCreateInfo.pStages = gbufferShaderStages.data(); + pipelineCreateInfo.layout = m_pipelineLayouts.gBuffer; + pipelineCreateInfo.renderPass = m_renderPasses.gBuffer; + pipelineCreateInfo.pVertexInputState = &m_inputStates.geometryStatic; + rasterizationState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + vkCreateGraphicsPipelines(m_renderer->device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &m_pipelines.geometryStatic); + + VkShaderModule vertexDynamicGeometryShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/deferred/dynamic.vert", + m_renderer->device.logicalDevice + ); + + gbufferShaderStages[0] = vk::initializers::ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexDynamicGeometryShaderModule); + pipelineCreateInfo.pVertexInputState = &m_inputStates.geometryDynamic; + pipelineCreateInfo.pStages = gbufferShaderStages.data(); + + vkCreateGraphicsPipelines(m_renderer->device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &m_pipelines.geometryDynamic); + + pipelineLayoutInfo.pSetLayouts = &m_descriptorLayouts.postProcessing; + vkCreatePipelineLayout(m_renderer->device.logicalDevice, &pipelineLayoutInfo, nullptr, &m_pipelineLayouts.postProcessing); + + VkShaderModule fragmentPostShaderModule = ShaderCompiler::CreateShaderModule( + "shaders/post.frag", + m_renderer->device.logicalDevice + ); + + std::array postShaderStages = { + vk::initializers::ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexQuadShaderModule), + vk::initializers::ShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentPostShaderModule) + }; + + pipelineCreateInfo.pVertexInputState = &m_inputStates.quad; + pipelineCreateInfo.pStages = postShaderStages.data(); + pipelineCreateInfo.renderPass = m_renderPasses.presentation; + pipelineCreateInfo.layout = m_pipelineLayouts.postProcessing; + colorBlendState = vk::initializers::ColorBlendState(1, colorBlendAttachments.data()); + rasterizationState.frontFace = VK_FRONT_FACE_CLOCKWISE; + + vkCreateGraphicsPipelines(m_renderer->device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &m_pipelines.postProcessing); + + vkDestroyShaderModule(m_renderer->device.logicalDevice, vertexLightingShaderModule, nullptr); + vkDestroyShaderModule(m_renderer->device.logicalDevice, fragmentLightingShaderModule, nullptr); + + vkDestroyShaderModule(m_renderer->device.logicalDevice, vertexGBufferShaderModule, nullptr); + vkDestroyShaderModule(m_renderer->device.logicalDevice, fragmentGBufferShaderModule, nullptr); + + vkDestroyShaderModule(m_renderer->device.logicalDevice, vertexDynamicGeometryShaderModule, nullptr); + + vkDestroyShaderModule(m_renderer->device.logicalDevice, vertexQuadShaderModule, nullptr); + vkDestroyShaderModule(m_renderer->device.logicalDevice, fragmentPostShaderModule, nullptr); + + vkDestroyShaderModule(m_renderer->device.logicalDevice, fragmentAmbientShaderModule, nullptr); +} + +void DeferredPipeline::SetupDescriptorPool() +{ + std::array poolSizes = { + vk::initializers::PoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + vk::initializers::PoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 35), + vk::initializers::PoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 50) + }; + + VkDescriptorPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolCreateInfo.maxSets = 15; + poolCreateInfo.poolSizeCount = static_cast(poolSizes.size()); + poolCreateInfo.pPoolSizes = poolSizes.data(); + + vkCreateDescriptorPool(m_renderer->device.logicalDevice, &poolCreateInfo, nullptr, &m_descriptorPool); +} + +void DeferredPipeline::SetupDescriptorSets() +{ + //ambient lighting + { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &m_descriptorLayouts.ambientLighting; + + vkAllocateDescriptorSets(m_renderer->device.logicalDevice, &allocInfo, &m_ambientDescriptorSet); + + VkDescriptorImageInfo albedoSamplerInfo = {}; + albedoSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + albedoSamplerInfo.imageView = m_gBuffer.albedo.view; + albedoSamplerInfo.sampler = m_gBuffer.colorSampler; + + VkDescriptorImageInfo materialSamplerInfo = {}; + materialSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + materialSamplerInfo.imageView = m_gBuffer.material.view; + materialSamplerInfo.sampler = m_gBuffer.colorSampler; + + std::array descriptorWrites = { + vk::initializers::DescriptorWrite(m_ambientDescriptorSet, 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &albedoSamplerInfo), + vk::initializers::DescriptorWrite(m_ambientDescriptorSet, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &materialSamplerInfo) + }; + + vkUpdateDescriptorSets(m_renderer->device.logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + } + + //point lighting + { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &m_descriptorLayouts.pointLighting; + + vkAllocateDescriptorSets(m_renderer->device.logicalDevice, &allocInfo, &m_lightingDescriptorSet); + + VkDescriptorBufferInfo sceneBufferInfo = {}; + sceneBufferInfo.buffer = m_uniformLightBuffer; + sceneBufferInfo.range = sizeof(UniformLightData); + sceneBufferInfo.offset = 0; + + VkDescriptorImageInfo positionSamplerInfo = {}; + positionSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + positionSamplerInfo.imageView = m_gBuffer.position.view; + positionSamplerInfo.sampler = m_gBuffer.colorSampler; + + VkDescriptorImageInfo normalSamplerInfo = {}; + normalSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + normalSamplerInfo.imageView = m_gBuffer.normal.view; + normalSamplerInfo.sampler = m_gBuffer.colorSampler; + + VkDescriptorImageInfo albedoSamplerInfo = {}; + albedoSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + albedoSamplerInfo.imageView = m_gBuffer.albedo.view; + albedoSamplerInfo.sampler = m_gBuffer.colorSampler; + + VkDescriptorImageInfo materialSamplerInfo = {}; + materialSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + materialSamplerInfo.imageView = m_gBuffer.material.view; + materialSamplerInfo.sampler = m_gBuffer.colorSampler; + + std::array descriptorWrites = { + vk::initializers::DescriptorWrite(m_lightingDescriptorSet, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, &sceneBufferInfo), + vk::initializers::DescriptorWrite(m_lightingDescriptorSet, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &positionSamplerInfo), + vk::initializers::DescriptorWrite(m_lightingDescriptorSet, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &normalSamplerInfo), + vk::initializers::DescriptorWrite(m_lightingDescriptorSet, 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &albedoSamplerInfo), + vk::initializers::DescriptorWrite(m_lightingDescriptorSet, 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &materialSamplerInfo) + }; + + vkUpdateDescriptorSets(m_renderer->device.logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + } + + //post processing + { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &m_descriptorLayouts.postProcessing; + + vkAllocateDescriptorSets(m_renderer->device.logicalDevice, &allocInfo, &m_postDescriptorSet); + + VkDescriptorBufferInfo postBufferInfo = {}; + postBufferInfo.buffer = m_uniformPostBuffer; + postBufferInfo.range = sizeof(UniformPostData); + postBufferInfo.offset = 0; + + VkDescriptorImageInfo sceneSamplerInfo = {}; + sceneSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + sceneSamplerInfo.imageView = m_finalResult.color.view; + sceneSamplerInfo.sampler = m_finalResult.colorSampler; + + std::array descriptorWrites = { + vk::initializers::DescriptorWrite(m_postDescriptorSet, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &postBufferInfo), + vk::initializers::DescriptorWrite(m_postDescriptorSet, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &sceneSamplerInfo), + vk::initializers::DescriptorWrite(m_postDescriptorSet, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &sceneSamplerInfo), + vk::initializers::DescriptorWrite(m_postDescriptorSet, 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &sceneSamplerInfo), + vk::initializers::DescriptorWrite(m_postDescriptorSet, 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &sceneSamplerInfo), + vk::initializers::DescriptorWrite(m_postDescriptorSet, 5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &sceneSamplerInfo) + }; + + vkUpdateDescriptorSets(m_renderer->device.logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + } +} + +void DeferredPipeline::CreateAttachment(VkFormat format, VkImageUsageFlagBits usage, Attachment &attachment) +{ + VkImageAspectFlags aspectFlags = 0; + + attachment.format = format; + + if(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + { + aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; + } + else if(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT; + } + + VkImageCreateInfo imageCreateInfo = vk::initializers::Image2D( + format, + m_renderer->swapchainExtent.width, + m_renderer->swapchainExtent.height, + usage | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_LAYOUT_UNDEFINED + ); + + vkCreateImage(m_renderer->device.logicalDevice, &imageCreateInfo, nullptr, &attachment.image); + + vk::BootstrapImage(attachment.image, attachment.memory, m_renderer->device); + + VkImageViewCreateInfo viewCreateInfo = vk::initializers::ImageView2D(format, aspectFlags, attachment.image); + vkCreateImageView(m_renderer->device.logicalDevice, &viewCreateInfo, nullptr, &attachment.view); +} + +void DeferredPipeline::DestroyAttachment(Attachment& attachment) +{ + vkDestroyImageView(m_renderer->device.logicalDevice, attachment.view, nullptr); + + vkFreeMemory(m_renderer->device.logicalDevice, attachment.memory, nullptr); + vkDestroyImage(m_renderer->device.logicalDevice, attachment.image, nullptr); +} + +void DeferredPipeline::ClearStaticGeometryBuffers() +{ + for(auto& command : m_staticDrawCommands) + { + vkFreeMemory(m_renderer->device.logicalDevice, command.modelBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.indexBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.vertexBufferMemory, nullptr); + vkFreeMemory(m_renderer->device.logicalDevice, command.indirectStateBufferMemory, nullptr); + + vkDestroyBuffer(m_renderer->device.logicalDevice, command.modelBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.indexBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.vertexBuffer, nullptr); + vkDestroyBuffer(m_renderer->device.logicalDevice, command.indirectStateBuffer, nullptr); + } +} + +void DeferredPipeline::CreateSet(Material* material) +{ + Texture* albedoTexture = AssetManager::Get(material->albedoID); + Texture* normalTexture = AssetManager::Get(material->normalID); + Texture* roughnessTexture = AssetManager::Get(material->roughnessID); + Texture* metallicTexture = AssetManager::Get(material->metallicID); + + VkDescriptorSet set; + if(m_samplerSets.count(material)) + { + set = m_samplerSets.at(material); + m_samplerSets.erase(material); + } + else + { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &m_descriptorLayouts.gBuffer; + + vkAllocateDescriptorSets(m_renderer->device.logicalDevice, &allocInfo, &set); + } + + VkDescriptorBufferInfo sceneBufferInfo = {}; + sceneBufferInfo.buffer = m_uniformObjectBuffer; + sceneBufferInfo.offset = 0; + sceneBufferInfo.range = sizeof(UniformObjectData); + + VkDescriptorImageInfo albedoSamplerInfo = {}; + albedoSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if(albedoTexture != nullptr && albedoTexture->IsLoaded()) + { + VulkanTexture vulkanTexture = m_renderer->GetTexture(albedoTexture); + + albedoSamplerInfo.sampler = vulkanTexture.sampler; + albedoSamplerInfo.imageView = vulkanTexture.view; + } + else + { + albedoSamplerInfo.sampler = m_placeholderImage.sampler; + albedoSamplerInfo.imageView = m_placeholderImage.view; + } + + VkDescriptorImageInfo normalSamplerInfo = {}; + normalSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if(normalTexture != nullptr && normalTexture->IsLoaded()) + { + VulkanTexture vulkanTexture = m_renderer->GetTexture(normalTexture); + + normalSamplerInfo.sampler = vulkanTexture.sampler; + normalSamplerInfo.imageView = vulkanTexture.view; + } + else + { + normalSamplerInfo.sampler = m_placeholderImage.sampler; + normalSamplerInfo.imageView = m_placeholderImage.view; + } + + VkDescriptorImageInfo roughnessSamplerInfo = {}; + roughnessSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if(roughnessTexture != nullptr && roughnessTexture->IsLoaded()) + { + VulkanTexture vulkanTexture = m_renderer->GetTexture(roughnessTexture); + + roughnessSamplerInfo.sampler = vulkanTexture.sampler; + roughnessSamplerInfo.imageView = vulkanTexture.view; + } + else + { + roughnessSamplerInfo.sampler = m_placeholderImage.sampler; + roughnessSamplerInfo.imageView = m_placeholderImage.view; + } + + VkDescriptorImageInfo metallicSamplerInfo = {}; + metallicSamplerInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if(metallicTexture != nullptr && metallicTexture->IsLoaded()) + { + VulkanTexture vulkanTexture = m_renderer->GetTexture(metallicTexture); + + metallicSamplerInfo.sampler = vulkanTexture.sampler; + metallicSamplerInfo.imageView = vulkanTexture.view; + } + else + { + metallicSamplerInfo.sampler = m_placeholderImage.sampler; + metallicSamplerInfo.imageView = m_placeholderImage.view; + } + + std::array descriptorWrites = { + vk::initializers::DescriptorWrite(set, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, &sceneBufferInfo), + vk::initializers::DescriptorWrite(set, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &albedoSamplerInfo), + vk::initializers::DescriptorWrite(set, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &normalSamplerInfo), + vk::initializers::DescriptorWrite(set, 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &metallicSamplerInfo), + vk::initializers::DescriptorWrite(set, 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &roughnessSamplerInfo) + }; + + vkUpdateDescriptorSets(m_renderer->device.logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + + m_samplerSets.emplace(material, set); + + material->invalidated = false; + + Log::Print("Cached material %s", material->GetName().c_str()); +} \ No newline at end of file diff --git a/engine/renderer/src/drawlist.cpp b/engine/renderer/src/drawlist.cpp new file mode 100644 index 0000000..8ad1cb0 --- /dev/null +++ b/engine/renderer/src/drawlist.cpp @@ -0,0 +1,104 @@ +#include "drawlist.hpp" + +#include +#include + +#include "renderer.hpp" + +std::function DrawList::onStaticGeometryUpdate; + +bool DrawList::m_frameInvalidated; + +void DrawList::RebuildDynamicLists() +{ + if(m_frameInvalidated) + RebuildStaticLists(); + + m_dynamicGeometry.clear(); + + auto renderers = GetEntPool().GetOf(); + for(size_t i = 0; i < renderers.size() && i < ResourceLimits::maxDynamicGeometry; i++) + { + if(!renderers[i]->isStaticGeometry) + { + Mesh* mesh = AssetManager::Get(renderers[i]->meshID); + Material* material = AssetManager::Get(renderers[i]->materialID); + + if(mesh != nullptr && mesh->IsLoaded() && material != nullptr) + { + DynamicGeometry* geometry = new DynamicGeometry; + geometry->name = renderers[i]->GetEntity()->name; + geometry->mesh = mesh; + geometry->material = material; + geometry->model = renderers[i]->GetEntity()->GetTransform()->GetModel(); + + m_dynamicGeometry.push_back(geometry); + } + } + } +} + +void DrawList::RebuildStaticLists() +{ + Log::Print("Rebuilding static display lists..."); + + m_staticGeometry = StaticGeometry(); + + auto renderers = GetEntPool().GetOf(); + for(size_t i = 0; i < renderers.size(); i++) + { + if(renderers[i]->isStaticGeometry) + { + Mesh* mesh = AssetManager::Get(renderers[i]->meshID); + Material* material = AssetManager::Get(renderers[i]->materialID); + + if(mesh != nullptr && material != nullptr) + { + if(!m_staticGeometry.drawCommands.count(material)) + m_staticGeometry.drawCommands.emplace(material, DrawCommand()); + + DrawCommand& command = m_staticGeometry.drawCommands.at(material); + + DrawCommand::Data geometryData; + geometryData.vertexCount = static_cast(mesh->GetIndices().size()); + geometryData.instanceCount = 1; + geometryData.firstVertex = static_cast(command.indices.size()); + geometryData.baseVertex = static_cast(command.vertices.size()); + geometryData.baseInstance = static_cast(command.geometryData.size()); + + command.geometryData.push_back(geometryData); + + //append mesh data + command.indices.insert(command.indices.end(), mesh->GetIndices().begin(), mesh->GetIndices().end()); + command.vertices.insert(command.vertices.end(), mesh->GetVertices().begin(), mesh->GetVertices().end()); + command.models.push_back(renderers[i]->GetEntity()->GetTransform()->GetModel()); + } + } + } + + m_frameInvalidated = false; + + if(onStaticGeometryUpdate) + onStaticGeometryUpdate(); +} + +StaticGeometry DrawList::GetStaticGeometry() +{ + return m_staticGeometry; +} + +//reserved for future use +std::vector DrawList::GetInstancedGeometry() +{ + return std::vector(); +} + +std::vector DrawList::GetDynamicGeometry() +{ + return m_dynamicGeometry; +} + +void DrawList::InvalidateNextFrame() +{ + m_frameInvalidated = true; +} \ No newline at end of file diff --git a/engine/renderer/src/imgui_wrapper.cpp b/engine/renderer/src/imgui_wrapper.cpp new file mode 100644 index 0000000..961699d --- /dev/null +++ b/engine/renderer/src/imgui_wrapper.cpp @@ -0,0 +1,76 @@ +#include "imgui_wrapper.hpp" + +#include +#include +#include +#include + +ImGuiWrapper::ImGuiWrapper(vk::Device& device) +{ + m_device = device; + + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = Key::Tab; + io.KeyMap[ImGuiKey_LeftArrow] = Key::LeftArrow; + io.KeyMap[ImGuiKey_RightArrow] = Key::RightArrow; + io.KeyMap[ImGuiKey_UpArrow] = Key::UpArrow; + io.KeyMap[ImGuiKey_DownArrow] = Key::DownArrow; + io.KeyMap[ImGuiKey_PageUp] = Key::PageUp; + io.KeyMap[ImGuiKey_PageDown] = Key::PageDown; + io.KeyMap[ImGuiKey_Home] = Key::Home; + io.KeyMap[ImGuiKey_End] = Key::End; + io.KeyMap[ImGuiKey_Delete] = Key::Delete; + io.KeyMap[ImGuiKey_Backspace] = Key::Backspace; + io.KeyMap[ImGuiKey_Enter] = Key::Enter; + io.KeyMap[ImGuiKey_Escape] = Key::Escape; + io.KeyMap[ImGuiKey_A] = Key::A; + io.KeyMap[ImGuiKey_C] = Key::C; + io.KeyMap[ImGuiKey_V] = Key::V; + io.KeyMap[ImGuiKey_X] = Key::X; + io.KeyMap[ImGuiKey_Y] = Key::Y; + io.KeyMap[ImGuiKey_Z] = Key::Z; + + using std::placeholders::_1; + using std::placeholders::_2; + Input::mouseButtonCallback = std::bind(&ImGuiWrapper::MouseButtonCallback, this, _1, _2); +} + +ImGuiWrapper::~ImGuiWrapper() +{ + ImGui::Shutdown(); +} + +void ImGuiWrapper::NewFrame() +{ + //if(!m_fontTexture) + // CreateGLObjects(); + + ImGuiIO& io = ImGui::GetIO(); + + io.DisplaySize = ImVec2(GFX::GetScreenWidth(), GFX::GetScreenHeight()); + io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); //TODO: add support for high dpi screens + io.DeltaTime = Time::GetDeltaTime(); + + double mouse_x, mouse_y; + Input::GetMousePosition(mouse_x, mouse_y); + io.MousePos = ImVec2(mouse_x, mouse_y); + + for (int i = 0; i < 3; i++) + { + io.MouseDown[i] = m_mousePressed[i] || Input::IsMouseDown(i) != 0; + m_mousePressed[i] = false; + } + + ImGui::NewFrame(); +} + +void ImGuiWrapper::RenderDrawLists() +{ + +} + +void ImGuiWrapper::MouseButtonCallback(int button, int action) +{ + if(action == 1 && button >= 0 && button < 3) + m_mousePressed[button] = true; +} \ No newline at end of file diff --git a/engine/renderer/src/primitives.cpp b/engine/renderer/src/primitives.cpp new file mode 100644 index 0000000..e390327 --- /dev/null +++ b/engine/renderer/src/primitives.cpp @@ -0,0 +1,211 @@ +#include "primitives.hpp" + +#include +#include +#include +#include + + +Sphere Primitives::sphere; +Quad Primitives::quad; + +void Primitives::Initialize(Renderer* renderer) +{ + //sphere + { + std::vector vertices; + std::vector indices; + + unsigned int xResolution = 64; + unsigned int yResolution = 64; + float PI = 3.14159265359f; + + for(unsigned int y = 0; y <= yResolution; ++y) + { + for(unsigned int x = 0; x <= xResolution; ++x) + { + float xSegment = static_cast(x) / static_cast(xResolution); + float ySegment = static_cast(y) / static_cast(yResolution); + + float xPos = glm::cos(xSegment * 2.0f * PI) * glm::sin(ySegment * PI); + float yPos = glm::cos(ySegment * PI); + float zPos = glm::sin(xSegment * 2.0f * PI) * glm::sin(ySegment * PI); + + vertices.push_back(glm::vec3(xPos, yPos, zPos)); + } + } + + bool oddRow = false; + for(unsigned int y = 0; y < yResolution; ++y) + { + if(!oddRow) { + for(unsigned int x = 0; x <= xResolution; ++x) + { + indices.push_back(y * (xResolution + 1) + x); + indices.push_back((y + 1) * (xResolution + 1) + x); + } + } + else + { + for(int x = xResolution; x >= 0; --x) + { + indices.push_back((y + 1) * (xResolution + 1) + x); + indices.push_back(y * (xResolution + 1) + x); + } + } + oddRow = !oddRow; + } + sphere.indexCount = static_cast(indices.size()); + + VkBuffer vertexStagingBuffer, indexStagingBuffer; + VkDeviceMemory vertexStagingBufferMemory, indexStagingBufferMemory; + + //VERTICES + VkDeviceSize vertexSize = sizeof(glm::vec3) * vertices.size(); + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexStagingBuffer, + vertexStagingBufferMemory, + renderer->device); + + void* data; + vkMapMemory(renderer->device.logicalDevice, vertexStagingBufferMemory, 0, vertexSize, 0, &data); + memcpy(data, vertices.data(), static_cast(vertexSize)); + vkUnmapMemory(renderer->device.logicalDevice, vertexStagingBufferMemory); + + //INDICES + VkDeviceSize indexSize = sizeof(unsigned int) * indices.size(); + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indexStagingBuffer, + indexStagingBufferMemory, + renderer->device); + + data = nullptr; + vkMapMemory(renderer->device.logicalDevice, indexStagingBufferMemory, 0, indexSize, 0, &data); + memcpy(data, indices.data(), static_cast(indexSize)); + vkUnmapMemory(renderer->device.logicalDevice, indexStagingBufferMemory); + + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + sphere.vertexBuffer, + sphere.vertexBufferMemory, + renderer->device); + vk::CopyBuffer(vertexStagingBuffer, sphere.vertexBuffer, vertexSize, renderer->device); + + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + sphere.indexBuffer, + sphere.indexBufferMemory, + renderer->device); + vk::CopyBuffer(indexStagingBuffer, sphere.indexBuffer, indexSize, renderer->device); + + vkFreeMemory(renderer->device.logicalDevice, vertexStagingBufferMemory, nullptr); + vkFreeMemory(renderer->device.logicalDevice, indexStagingBufferMemory, nullptr); + + vkDestroyBuffer(renderer->device.logicalDevice, vertexStagingBuffer, nullptr); + vkDestroyBuffer(renderer->device.logicalDevice, indexStagingBuffer, nullptr); + } + + //quad + { + std::vector vertices = { + { -1.0f, -1.0f, 0.0f }, + { 1.0f, -1.0f, 0.0f }, + { 1.0f, 1.0f, 0.0f }, + { -1.0f, 1.0f, 0.0f } + }; + + std::vector indices = { 0, 1, 2, 2, 3, 0 }; + + VkBuffer vertexStagingBuffer, indexStagingBuffer; + VkDeviceMemory vertexStagingBufferMemory, indexStagingBufferMemory; + + //VERTICES + VkDeviceSize vertexSize = sizeof(glm::vec3) * vertices.size(); + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexStagingBuffer, + vertexStagingBufferMemory, + renderer->device); + + void* data; + vkMapMemory(renderer->device.logicalDevice, vertexStagingBufferMemory, 0, vertexSize, 0, &data); + memcpy(data, vertices.data(), static_cast(vertexSize)); + vkUnmapMemory(renderer->device.logicalDevice, vertexStagingBufferMemory); + + //INDICES + VkDeviceSize indexSize = sizeof(unsigned int) * indices.size(); + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indexStagingBuffer, + indexStagingBufferMemory, + renderer->device); + + data = nullptr; + vkMapMemory(renderer->device.logicalDevice, indexStagingBufferMemory, 0, indexSize, 0, &data); + memcpy(data, indices.data(), static_cast(indexSize)); + vkUnmapMemory(renderer->device.logicalDevice, indexStagingBufferMemory); + + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + quad.vertexBuffer, + quad.vertexBufferMemory, + renderer->device); + vk::CopyBuffer(vertexStagingBuffer, quad.vertexBuffer, vertexSize, renderer->device); + + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + quad.indexBuffer, + quad.indexBufferMemory, + renderer->device); + vk::CopyBuffer(indexStagingBuffer, quad.indexBuffer, indexSize, renderer->device); + + vkFreeMemory(renderer->device.logicalDevice, vertexStagingBufferMemory, nullptr); + vkFreeMemory(renderer->device.logicalDevice, indexStagingBufferMemory, nullptr); + + vkDestroyBuffer(renderer->device.logicalDevice, vertexStagingBuffer, nullptr); + vkDestroyBuffer(renderer->device.logicalDevice, indexStagingBuffer, nullptr); + } +} + +void Primitives::Cleanup(Renderer *renderer) +{ + vkFreeMemory(renderer->device.logicalDevice, sphere.indexBufferMemory, nullptr); + vkFreeMemory(renderer->device.logicalDevice, sphere.vertexBufferMemory, nullptr); + vkFreeMemory(renderer->device.logicalDevice, quad.indexBufferMemory, nullptr); + vkFreeMemory(renderer->device.logicalDevice, quad.vertexBufferMemory, nullptr); + + vkDestroyBuffer(renderer->device.logicalDevice, sphere.indexBuffer, nullptr); + vkDestroyBuffer(renderer->device.logicalDevice, sphere.vertexBuffer, nullptr); + vkDestroyBuffer(renderer->device.logicalDevice, quad.indexBuffer, nullptr); + vkDestroyBuffer(renderer->device.logicalDevice, quad.vertexBuffer, nullptr); +} + +void Primitives::DrawQuad(VkCommandBuffer commandBuffer) +{ + VkDeviceSize offsets[] = { 0 }; + + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &quad.vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, quad.indexBuffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdDrawIndexed(commandBuffer, 6, 1, 0, 0, 0); +} + +void Primitives::DrawSphere(VkCommandBuffer commandBuffer) +{ + VkDeviceSize offsets[] = { 0 }; + + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &sphere.vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, sphere.indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(commandBuffer, sphere.indexCount, 1, 0, 0, 0); +} \ No newline at end of file diff --git a/engine/renderer/src/renderer.cpp b/engine/renderer/src/renderer.cpp new file mode 100644 index 0000000..e637d51 --- /dev/null +++ b/engine/renderer/src/renderer.cpp @@ -0,0 +1,415 @@ +#include "renderer.hpp" + +#include +#include +#include + +#include "deferredpipeline.hpp" +#include "primitives.hpp" +#include "imgui_wrapper.hpp" + +bool initialized = false; + +Renderer::Renderer(Engine* engine, RequestedFeatures features) : m_engine(engine) +{ + initialized = false; + + Log::Print("Initializing Vulkan renderer..."); + + surface = GFX::CreateVulkanSurface(instance); + + device = vk::Device(vk::GetPhysicalDevices(instance)[0]); + + VkPhysicalDeviceFeatures enabledFeatures = {}; + + //TODO: is independent blend actually needed? + if(device.deviceFeatures.independentBlend) + enabledFeatures.independentBlend = VK_TRUE; + + device.CreateLogical(enabledFeatures); + + //TODO: hook this up to something + VkBool32 presentSupported; + vkGetPhysicalDeviceSurfaceSupportKHR(device.physicalDevice, 0, surface, &presentSupported); + + CreateSwapchain(); + AllocateCommandBuffers(); + + pipeline = new DeferredPipeline(this); + + //sync objects + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + vkCreateSemaphore(device.logicalDevice, &semaphoreInfo, nullptr, &imageAvailableSemaphore); + + VkFenceCreateInfo fenceCreateInfo = {}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + + vkCreateFence(device.logicalDevice, &fenceCreateInfo, nullptr, &submitFence); + + Primitives::Initialize(this); + + /*if(debugEnabled) + m_imWrapper = new ImGuiWrapper(window, device);*/ + + if(features.imgui) + m_imWrapper = new ImGuiWrapper(device); + + initialized = true; +} + +void Renderer::NewFrame() +{ + //if(m_imWrapper) + // m_imWrapper->NewFrame(); +} + +void Renderer::Draw() +{ + PrepareFrame(); + + if(onRender) + onRender(); + + for(uint32_t i = 0; i < commandBuffers.size(); i++) + pipeline->Record(commandBuffers[i], i); + + SubmitInfo si; + si.ias = imageAvailableSemaphore; + si.cmdBuf = commandBuffers[imageIndex]; + si.submitFence = submitFence; + + pipeline->Submit(si); + + SubmitFrame(); +} + +void Renderer::CreateSwapchain() +{ + //swapchain + SwapChainSupportDetails swapChainSupport = vk::QuerySwapChainSupport(device.physicalDevice, surface); + + VkSurfaceFormatKHR surfaceFormat = vk::ChooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = vk::ChooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = vk::ChooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if(swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) + { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR swapchainCreateInfo = {}; + swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCreateInfo.surface = surface; + swapchainCreateInfo.minImageCount = imageCount; + swapchainCreateInfo.imageFormat = surfaceFormat.format; + swapchainCreateInfo.imageColorSpace = surfaceFormat.colorSpace; + swapchainCreateInfo.imageExtent = extent; + swapchainCreateInfo.imageArrayLayers = 1; + swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCreateInfo.preTransform = swapChainSupport.capabilities.currentTransform; + swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainCreateInfo.presentMode = presentMode; + swapchainCreateInfo.clipped = VK_TRUE; + swapchainCreateInfo.oldSwapchain = nullptr; + + vkCreateSwapchainKHR(device.logicalDevice, &swapchainCreateInfo, nullptr, &swapchain); + + vkGetSwapchainImagesKHR(device.logicalDevice, swapchain, &imageCount, nullptr); + swapchainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device.logicalDevice, swapchain, &imageCount, swapchainImages.data()); + + swapchainImageFormat = surfaceFormat.format; + swapchainExtent = extent; + + //swapchain image views + swapchainImageViews.resize(swapchainImages.size()); + + for(unsigned int i = 0; i < swapchainImages.size(); i++) + { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapchainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapchainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(device.logicalDevice, &createInfo, nullptr, &swapchainImageViews[i]); + } +} + +void Renderer::AllocateCommandBuffers() +{ + //command buffers + commandBuffers.resize(3); + + VkCommandBufferAllocateInfo bufferAllocInfo = {}; + bufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + bufferAllocInfo.commandPool = device.commandPool; + bufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + bufferAllocInfo.commandBufferCount = static_cast(commandBuffers.size()); + + vkAllocateCommandBuffers(device.logicalDevice, &bufferAllocInfo, commandBuffers.data()); +} + +void Renderer::PrepareFrame() +{ + vkAcquireNextImageKHR(device.logicalDevice, swapchain, std::numeric_limits::max(), imageAvailableSemaphore, nullptr, &imageIndex); +} + +void Renderer::SubmitFrame() +{ + vkWaitForFences(device.logicalDevice, 1, &submitFence, VK_TRUE, UINT64_MAX); + + vkResetFences(device.logicalDevice, 1, &submitFence); + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &pipeline->m_renderFinishedSemaphore; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &swapchain; + presentInfo.pImageIndices = &imageIndex; + + vkQueuePresentKHR(device.graphicsQueue, &presentInfo); +} + +void Renderer::Resized() +{ + if(!initialized) + return; + + vkDeviceWaitIdle(device.logicalDevice); + + for(unsigned int i = 0; i < swapchainImages.size(); i++) + vkDestroyImageView(device.logicalDevice, swapchainImageViews[i], nullptr); + + delete pipeline; + + vkDestroySwapchainKHR(device.logicalDevice, swapchain, nullptr); + + CreateSwapchain(); + + pipeline = new DeferredPipeline(this); +} + +void Renderer::SynthesizeMesh(Mesh *mesh) +{ + VulkanMesh vulkanMesh; + + //VERTICES + VkDeviceMemory vertexStagingBufferMemory; + VkBuffer vertexStagingBuffer; + + VkDeviceSize vertexSize = sizeof(Vertex) * mesh->GetVertices().size(); + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexStagingBuffer, + vertexStagingBufferMemory, + device); + + void* data; + vkMapMemory(device.logicalDevice, vertexStagingBufferMemory, 0, vertexSize, 0, &data); + memcpy(data, mesh->GetVertices().data(), static_cast(vertexSize)); + vkUnmapMemory(device.logicalDevice, vertexStagingBufferMemory); + + //INDICES + VkDeviceMemory indexStagingBufferMemory; + VkBuffer indexStagingBuffer; + + VkDeviceSize indexSize = sizeof(unsigned int) * mesh->GetIndices().size(); + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indexStagingBuffer, + indexStagingBufferMemory, + device); + + data = nullptr; + vkMapMemory(device.logicalDevice, indexStagingBufferMemory, 0, indexSize, 0, &data); + memcpy(data, mesh->GetIndices().data(), static_cast(indexSize)); + vkUnmapMemory(device.logicalDevice, indexStagingBufferMemory); + + //buffer upload + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + vulkanMesh.vertexBuffer, + vulkanMesh.vertexBufferMemory, + device); + + vk::CopyBuffer(vertexStagingBuffer, vulkanMesh.vertexBuffer, vertexSize, device); + + vk::CreateBuffer(indexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + vulkanMesh.indexBuffer, + vulkanMesh.indexBufferMemory, + device); + + vk::CopyBuffer(indexStagingBuffer, vulkanMesh.indexBuffer, indexSize, device); + + vkFreeMemory(device.logicalDevice, vertexStagingBufferMemory, nullptr); + vkFreeMemory(device.logicalDevice, indexStagingBufferMemory, nullptr); + + vkDestroyBuffer(device.logicalDevice, vertexStagingBuffer, nullptr); + vkDestroyBuffer(device.logicalDevice, indexStagingBuffer, nullptr); + + Log::Print("Synthesized mesh %s", mesh->GetName().c_str()); + + m_vulkanMeshes.emplace(mesh, vulkanMesh); +} + +void Renderer::ReleaseMesh(Mesh *mesh) +{ + VulkanMesh vulkanMesh = m_vulkanMeshes.at(mesh); + + vkFreeMemory(device.logicalDevice, vulkanMesh.vertexBufferMemory, nullptr); + vkFreeMemory(device.logicalDevice, vulkanMesh.indexBufferMemory, nullptr); + + vkDestroyBuffer(device.logicalDevice, vulkanMesh.vertexBuffer, nullptr); + vkDestroyBuffer(device.logicalDevice, vulkanMesh.indexBuffer, nullptr); + + m_vulkanMeshes.erase(mesh); +} + +VulkanMesh Renderer::GetMesh(Mesh *mesh) +{ + return m_vulkanMeshes.at(mesh); +} + +void Renderer::SynthesizeTexture(Texture *texture) +{ + VulkanTexture vulkanTexture; + + VkDeviceSize imageSize = static_cast(texture->width * texture->height * 4); + + VkImage stagingImage; + VkDeviceMemory stagingImageMemory; + vk::createImage(static_cast(texture->width), + static_cast(texture->height), + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingImage, + stagingImageMemory, + device); + + void *data; + vkMapMemory(device.logicalDevice, stagingImageMemory, 0, imageSize, 0, &data); + memcpy(data, texture->pixels, static_cast(imageSize)); + vkUnmapMemory(device.logicalDevice, stagingImageMemory); + + vk::createImage(static_cast(texture->width), + static_cast(texture->height), + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + vulkanTexture.image, + vulkanTexture.memory, + device); + + vk::transitionImageLayout(stagingImage, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, device); + vk::transitionImageLayout(vulkanTexture.image, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, device); + + vk::copyImage(stagingImage, + vulkanTexture.image, + static_cast(texture->width), + static_cast(texture->height), + device); + + vk::transitionImageLayout(vulkanTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, device); + + vkFreeMemory(device.logicalDevice, stagingImageMemory, nullptr); + vkDestroyImage(device.logicalDevice, stagingImage, nullptr); + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = vulkanTexture.image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(device.logicalDevice, &viewInfo, nullptr, &vulkanTexture.view); + + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + vkCreateSampler(device.logicalDevice, &samplerInfo, nullptr, &vulkanTexture.sampler); + + Log::Print("Synthesized texture %s", texture->GetName().c_str()); + + m_vulkanTextures.emplace(texture, vulkanTexture); +} + +void Renderer::ReleaseTexture(Texture *texture) +{ + VulkanTexture vulkanTexture = m_vulkanTextures.at(texture); + + vkDestroySampler(device.logicalDevice, vulkanTexture.sampler, nullptr); + vkDestroyImageView(device.logicalDevice, vulkanTexture.view, nullptr); + + vkFreeMemory(device.logicalDevice, vulkanTexture.memory, nullptr); + vkDestroyImage(device.logicalDevice, vulkanTexture.image, nullptr); + + m_vulkanTextures.erase(texture); +} + +VulkanTexture Renderer::GetTexture(Texture *texture) +{ + return m_vulkanTextures.at(texture); +} + +Renderer::~Renderer() +{ + vkDeviceWaitIdle(device.logicalDevice); + + vkDestroyFence(device.logicalDevice, submitFence, nullptr); + vkDestroySemaphore(device.logicalDevice, imageAvailableSemaphore, nullptr); + + delete pipeline; + + Primitives::Cleanup(this); + + for(unsigned int i = 0; i < swapchainImages.size(); i++) + vkDestroyImageView(device.logicalDevice, swapchainImageViews[i], nullptr); + + vkDestroySwapchainKHR(device.logicalDevice, swapchain, nullptr); + + vkDestroySurfaceKHR(instance, surface, nullptr); + + Log::Print("Cleaned up renderer!"); +} \ No newline at end of file diff --git a/engine/renderer/src/shadercompiler.cpp b/engine/renderer/src/shadercompiler.cpp new file mode 100644 index 0000000..9456118 --- /dev/null +++ b/engine/renderer/src/shadercompiler.cpp @@ -0,0 +1,227 @@ +#include "shadercompiler.hpp" + +#include +#include +#include +#include + +class Includer : public glslang::TShader::Includer +{ +public: + IncludeResult* includeLocal(const char* headerName, + const char* includerName, + size_t inclusionDepth) override; + + void releaseInclude(IncludeResult* result) override; + ~Includer(); + +private: + static std::map includes; + static std::vector included_files; +}; + +std::map Includer::includes; +std::vector Includer::included_files; + +constexpr TBuiltInResource DefaultResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +VkShaderModule ShaderCompiler::CreateShaderModule(const std::string& path, VkDevice logicalDevice) +{ + std::string extension = path.substr(path.find_last_of(".") + 1, path.length()); + + int size; + File file = Filesystem::GetFileHandle(path, size, true); + if(size > 0) + { + std::string shader_prefix = "#version 450\n#extension GL_ARB_separate_shader_objects : enable\n#extension GL_GOOGLE_include_directive : enable\n\n"; + + std::string filestring = shader_prefix + file; + + glslang::InitializeProcess(); + + const char* shaderStrings = filestring.c_str(); + const int shaderLengths = static_cast(filestring.size()); + + EShLanguage stage = EShLangVertex; + if(extension == "vert") + { + stage = EShLangVertex; + } + else if(extension == "frag") + { + stage = EShLangFragment; + } + + glslang::TShader* shader = new glslang::TShader(stage); + shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); + + Includer includer; + if(!shader->parse(&DefaultResource, 450, ECoreProfile, true, false, EShMessages::EShMsgCascadingErrors, includer)) + { + Log::Error("Failed to parse shader %s!", path.c_str()); + Log::Print("Parse errors: %s", shader->getInfoLog()); + + return VkShaderModule(); + } + + glslang::TProgram* program = new glslang::TProgram(); + program->addShader(shader); + if(!program->link(EShMsgDefault)) + { + Log::Error("Failed to link program %s", path.c_str()); + Log::Print("Link errors: %s", program->getInfoLog()); + + return VkShaderModule(); + } + + std::vector spirv; + spv::SpvBuildLogger logger; + + glslang::GlslangToSpv(*program->getIntermediate(stage), spirv, &logger); + + VkShaderModuleCreateInfo shaderModuleCreateInfo = {}; + shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderModuleCreateInfo.codeSize = spirv.size() * sizeof(unsigned int); + shaderModuleCreateInfo.pCode = spirv.data(); + + VkShaderModule shaderModule; + vkCreateShaderModule(logicalDevice, &shaderModuleCreateInfo, nullptr, &shaderModule); + + //TODO: possible memory leak, look at making it a ref/parameter instead + return shaderModule; + } + else + { + Log::Error("Could not find shader file %s", path.c_str()); + + return VkShaderModule(); + } +} + +Includer::IncludeResult* Includer::includeLocal(const char* headerName, + const char* includerName, + size_t inclusionDepth) +{ + Log::Print("Including file %s", headerName); + + std::string app_path = "shaders/"; + + int size; + File file = Filesystem::GetFileHandle(app_path + headerName, size, true); + if(size > 0) + { + included_files.push_back(file); + + return new IncludeResult(headerName, included_files.back(), size, nullptr); + } + else + { + Log::Error("Couldn't include shader file: %s", (app_path + headerName).c_str()); + + return new IncludeResult("Failed to include file", nullptr, 0, nullptr); + } +} + +void Includer::releaseInclude(IncludeResult* result) +{ + delete result; +} + +Includer::~Includer() +{ + included_files.clear(); +} \ No newline at end of file diff --git a/engine/renderer/src/shadowpass.cpp b/engine/renderer/src/shadowpass.cpp new file mode 100644 index 0000000..8883ffe --- /dev/null +++ b/engine/renderer/src/shadowpass.cpp @@ -0,0 +1,475 @@ +#include "shadowpass.hpp" + +#include +#include +#include +#include +#include +#include + +void ShadowPass::Initialize(Renderer* inRenderer, vk::Device device) +{ + renderer = inRenderer; + + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + vkCreateDescriptorSetLayout(device.logicalDevice, &layoutInfo, nullptr, &descriptorSetLayout); + + VkAttachmentDescription depthAttachment = {}; + depthAttachment.format = VK_FORMAT_D16_UNORM; + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthAttachmentRef = {}; + depthAttachmentRef.attachment = 0; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subPass = {}; + subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subPass.colorAttachmentCount = 0; + subPass.pDepthStencilAttachment = &depthAttachmentRef; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = 1; + renderPassCreateInfo.pAttachments = &depthAttachment; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subPass; + renderPassCreateInfo.dependencyCount = 0; + renderPassCreateInfo.pDependencies = nullptr; + + vkCreateRenderPass(device.logicalDevice, &renderPassCreateInfo, nullptr, &renderPass); + + vertexShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/shadow.vert", device.logicalDevice); + fragmentShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/shadow.frag", device.logicalDevice); + + VkPipelineShaderStageCreateInfo vertexStageCreateInfo; + vertexStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexStageCreateInfo.pNext = nullptr; + vertexStageCreateInfo.flags = 0; + vertexStageCreateInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexStageCreateInfo.module = vertexShaderModule; + vertexStageCreateInfo.pName = "main"; + vertexStageCreateInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo fragmentStageCreateInfo; + fragmentStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentStageCreateInfo.pNext = nullptr; + fragmentStageCreateInfo.flags = 0; + fragmentStageCreateInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragmentStageCreateInfo.module = fragmentShaderModule; + fragmentStageCreateInfo.pName = "main"; + fragmentStageCreateInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertexStageCreateInfo, fragmentStageCreateInfo}; + + VkVertexInputBindingDescription vertexBindingDescription; + vertexBindingDescription.binding = 0; + vertexBindingDescription.stride = sizeof(glm::vec3); + vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription vertexAttributeDescription; + vertexAttributeDescription.binding = 0; + vertexAttributeDescription.location = 0; + vertexAttributeDescription.format = VK_FORMAT_R32G32B32_SFLOAT; + vertexAttributeDescription.offset = 0; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.pNext = nullptr; + vertexInputInfo.flags = 0; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &vertexAttributeDescription; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.pNext = nullptr; + inputAssembly.flags = 0; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = 1000; + viewport.height = 1000; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor; + scissor.offset = {0, 0}; + scissor.extent = {1000, 1000}; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.pNext = nullptr; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.pNext = nullptr; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_TRUE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.pNext = nullptr; + multisampling.flags = 0; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendStateCreateInfo colorBlending; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.pNext = nullptr; + colorBlending.flags = 0; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 0; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + std::vector setLayouts = {descriptorSetLayout}; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.pNext = nullptr; + pipelineLayoutInfo.flags = 0; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = setLayouts.data(); + + vkCreatePipelineLayout(device.logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout); + + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + + VkDynamicState states[] = {VK_DYNAMIC_STATE_DEPTH_BIAS}; + + VkPipelineDynamicStateCreateInfo dynamicState = {}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = 1; + dynamicState.pDynamicStates = states; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.stageCount = 2; + pipelineCreateInfo.pStages = shaderStages; + pipelineCreateInfo.pVertexInputState = &vertexInputInfo; + pipelineCreateInfo.pInputAssemblyState = &inputAssembly; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pRasterizationState = &rasterizer; + pipelineCreateInfo.pMultisampleState = &multisampling; + pipelineCreateInfo.pDepthStencilState = &depthStencil; + pipelineCreateInfo.pColorBlendState = &colorBlending; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.layout = pipelineLayout; + pipelineCreateInfo.renderPass = renderPass; + + vkCreateGraphicsPipelines(device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + + VkDescriptorPoolSize poolSize = {}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + poolSize.descriptorCount = 1; + + VkDescriptorPoolSize sizes[] = {poolSize}; + + VkDescriptorPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = sizes; + poolInfo.maxSets = 1; + + vkCreateDescriptorPool(device.logicalDevice, &poolInfo, nullptr, &descriptorPool); + + VkDescriptorSetLayout layouts[] = {descriptorSetLayout}; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = layouts; + + vkAllocateDescriptorSets(device.logicalDevice, &allocInfo, &descriptorSet); + + vk::CreateBuffer(sizeof(UniformObjectData) * 15, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uniformObjectBuffer, + uniformObjectBufferMemory, + device); + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = uniformObjectBuffer; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformObjectData); + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets(device.logicalDevice, 1, &descriptorWrite, 0, nullptr); + + VkCommandBufferAllocateInfo bufferAllocInfo = {}; + bufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + bufferAllocInfo.commandPool = device.commandPool; + bufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + bufferAllocInfo.commandBufferCount = 1; + + vkAllocateCommandBuffers(device.logicalDevice, &bufferAllocInfo, &commandBuffer); + + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + vkCreateSemaphore(device.logicalDevice, &semaphoreInfo, nullptr, &semaphore); +} + +bool ShadowPass::IsLightPrepared(Light* light) +{ + if(lights.count(light)) + return true; + + return false; +} + +ShadowData ShadowPass::GetLightShadow(Light* light) +{ + return lights[light]; +} + +void ShadowPass::PrepareLight(Light *light, vk::Device device) +{ + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(device.physicalDevice, &memProperties); + + ShadowData data; + + VkImageCreateInfo imageInfo = {}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = 1000; + imageInfo.extent.height = 1000; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.format = VK_FORMAT_D16_UNORM; + imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + + vkCreateImage(device.logicalDevice, &imageInfo, nullptr, &data.image); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(device.logicalDevice, data.image, &memRequirements); + + VkMemoryAllocateInfo iallocInfo = {}; + iallocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + iallocInfo.allocationSize = memRequirements.size; + iallocInfo.memoryTypeIndex = vk::FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memProperties); + + vkAllocateMemory(device.logicalDevice, &iallocInfo, nullptr, &data.memory); + vkBindImageMemory(device.logicalDevice, data.image, data.memory, 0); + + //the image view for the framebuffer + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_D16_UNORM; + viewInfo.subresourceRange = {}; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + viewInfo.image = data.image; + + vkCreateImageView(device.logicalDevice, &viewInfo, nullptr, &data.view); + + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.maxAnisotropy = 0; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 1.0f; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + + vkCreateSampler(device.logicalDevice, &samplerInfo, nullptr, &data.sampler); + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = &data.view; + framebufferInfo.width = 1000; + framebufferInfo.height = 1000; + framebufferInfo.layers = 1; + + vkCreateFramebuffer(device.logicalDevice, &framebufferInfo, nullptr, &data.framebuffer); + + lights.emplace(light, data); +} + +void ShadowPass::Record(vk::Device device) +{ + has_recorded = false; + + std::vector cameras = GetEntPool().GetOf(); + if(cameras.size() == 0) + return; + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + int minUniformBufferOffsetAlignment = vk::GetUniformOffsetAlignment(device.physicalDevice); + + for(auto &itr : lights) + { + //TODO: implement a more robust system for removing lights from this list + if(!itr.first->shadowsEnabled) + continue; + + VkClearValue clearValue; + clearValue.depthStencil = {1.0f, 0}; + + //offscreen pass + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = itr.second.framebuffer; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent.width = 1000; + renderPassInfo.renderArea.extent.height = 1000; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearValue; + + float depthBiasConstant = 1.25f; + float depthBiasSlope = 1.75f; + + vkCmdSetDepthBias(commandBuffer, depthBiasConstant, 0.0f, depthBiasSlope); + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + auto renderers = GetEntPool().GetOf(); + for(unsigned int r = 0; r < renderers.size(); r++) + { + Mesh *mesh = AssetManager::Get(renderers[r]->meshID); + if(mesh != nullptr) + { + if(mesh->IsLoaded()) + { + uint32_t offset = r * Utility::Align(sizeof(UniformObjectData), minUniformBufferOffsetAlignment); + + //temporary fix + if(itr.first->GetEntity()->GetTransform() == nullptr) + break; + + UniformObjectData umd; + umd.view = glm::inverse(itr.first->GetEntity()->GetTransform()->GetModel()); + umd.proj = glm::ortho(-itr.first->shadowFrustumSize, + itr.first->shadowFrustumSize, + -itr.first->shadowFrustumSize, + itr.first->shadowFrustumSize, + 1.0f, + itr.first->shadowZFar); + umd.proj[1][1] *= -1; + umd.model = renderers[r]->GetEntity()->GetTransform()->GetModel(); + + void *data = nullptr; + vkMapMemory(device.logicalDevice, uniformObjectBufferMemory, offset, sizeof(UniformObjectData), 0, &data); + memcpy(data, &umd, sizeof(UniformObjectData)); + vkUnmapMemory(device.logicalDevice, uniformObjectBufferMemory); + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 1, &offset); + + VulkanMesh vulkanMesh = renderer->GetMesh(mesh); + + VkDeviceSize offsets[] = {0}; + + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vulkanMesh.vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, vulkanMesh.indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(commandBuffer, static_cast(mesh->GetIndices().size()), 1, 0, 0, 0); + } + } + } + + vkCmdEndRenderPass(commandBuffer); + + has_recorded = true; + } + + vkEndCommandBuffer(commandBuffer); +} + +void ShadowPass::Cleanup(vk::Device device) +{ + vkDestroySemaphore(device.logicalDevice, semaphore, nullptr); + + for(auto &itr : lights) + { + vkDestroyFramebuffer(device.logicalDevice, itr.second.framebuffer, nullptr); + vkDestroySampler(device.logicalDevice, itr.second.sampler, nullptr); + vkDestroyImageView(device.logicalDevice, itr.second.view, nullptr); + vkFreeMemory(device.logicalDevice, itr.second.memory, nullptr); + vkDestroyImage(device.logicalDevice, itr.second.image, nullptr); + } + + vkDestroyDescriptorPool(device.logicalDevice, descriptorPool, nullptr); + + vkFreeMemory(device.logicalDevice, uniformObjectBufferMemory, nullptr); + vkDestroyBuffer(device.logicalDevice, uniformObjectBuffer, nullptr); + + vkDestroyPipeline(device.logicalDevice, pipeline, nullptr); + vkDestroyPipelineLayout(device.logicalDevice, pipelineLayout, nullptr); + + vkDestroyShaderModule(device.logicalDevice, vertexShaderModule, nullptr); + vkDestroyShaderModule(device.logicalDevice, fragmentShaderModule, nullptr); + + vkDestroyRenderPass(device.logicalDevice, renderPass, nullptr); + + vkDestroyDescriptorSetLayout(device.logicalDevice, descriptorSetLayout, nullptr); +} diff --git a/engine/renderer/src/skyboxpass.cpp b/engine/renderer/src/skyboxpass.cpp new file mode 100644 index 0000000..62c36fd --- /dev/null +++ b/engine/renderer/src/skyboxpass.cpp @@ -0,0 +1,326 @@ +#include "skyboxpass.hpp" + +#include +#include +#include +#include + +void SkyboxPass::Initialize(VkRenderPass renderPass, vk::Device device) +{ + vertexShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/skybox.vert", device.logicalDevice); + fragmentShaderModule = ShaderCompiler::CreateShaderModule("game/shaders/skybox.frag", device.logicalDevice); + + VkDescriptorSetLayoutBinding matrixLayoutBinding = {}; + matrixLayoutBinding.binding = 0; + matrixLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + matrixLayoutBinding.descriptorCount = 1; + matrixLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayoutBinding sceneLayoutBinding = {}; + sceneLayoutBinding.binding = 1; + sceneLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + sceneLayoutBinding.descriptorCount = 1; + sceneLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding bindings[] = { matrixLayoutBinding, sceneLayoutBinding }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 2; + layoutInfo.pBindings = bindings; + + vkCreateDescriptorSetLayout(device.logicalDevice, &layoutInfo, nullptr, &descriptorSetLayout); + + VkPipelineShaderStageCreateInfo vertexStageCreateInfo; + vertexStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexStageCreateInfo.pNext = nullptr; + vertexStageCreateInfo.flags = 0; + vertexStageCreateInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexStageCreateInfo.module = vertexShaderModule; + vertexStageCreateInfo.pName = "main"; + vertexStageCreateInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo fragmentStageCreateInfo; + fragmentStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentStageCreateInfo.pNext = nullptr; + fragmentStageCreateInfo.flags = 0; + fragmentStageCreateInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragmentStageCreateInfo.module = fragmentShaderModule; + fragmentStageCreateInfo.pName = "main"; + fragmentStageCreateInfo.pSpecializationInfo = nullptr; + + std::vector shaderStages = { vertexStageCreateInfo, fragmentStageCreateInfo }; + + VkVertexInputBindingDescription vertexBindingDescription; + vertexBindingDescription.binding = 0; + vertexBindingDescription.stride = sizeof(glm::vec3); + vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription vertexAttributeDescription; + vertexAttributeDescription.binding = 0; + vertexAttributeDescription.location = 0; + vertexAttributeDescription.format = VK_FORMAT_R32G32B32_SFLOAT; + vertexAttributeDescription.offset = 0; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.pNext = nullptr; + vertexInputInfo.flags = 0; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &vertexAttributeDescription; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.pNext = nullptr; + inputAssembly.flags = 0; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = GFX::GetScreenWidth(); + viewport.height = GFX::GetScreenHeight(); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor; + scissor.offset = {0, 0}; + scissor.extent.width = GFX::GetScreenWidth(); + scissor.extent.height = GFX::GetScreenHeight(); + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.pNext = nullptr; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.pNext = nullptr; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.pNext = nullptr; + multisampling.flags = 0; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState brightBlendAttachment = {}; + brightBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + brightBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState attachmentStates[] = { colorBlendAttachment, brightBlendAttachment }; + + VkPipelineColorBlendStateCreateInfo colorBlending; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.pNext = nullptr; + colorBlending.flags = 0; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 2; + colorBlending.pAttachments = attachmentStates; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.pNext = nullptr; + pipelineLayoutInfo.flags = 0; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + + vkCreatePipelineLayout(device.logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout); + + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_FALSE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.stageCount = 2; + pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.pVertexInputState = &vertexInputInfo; + pipelineCreateInfo.pInputAssemblyState = &inputAssembly; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pRasterizationState = &rasterizer; + pipelineCreateInfo.pMultisampleState = &multisampling; + pipelineCreateInfo.pDepthStencilState = &depthStencil; + pipelineCreateInfo.pColorBlendState = &colorBlending; + pipelineCreateInfo.layout = pipelineLayout; + pipelineCreateInfo.renderPass = renderPass; + + vkCreateGraphicsPipelines(device.logicalDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + + vk::CreateBuffer(sizeof(UniformMatrixData), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uniformMatrixBuffer, + uniformMatrixBufferMemory, + device); + + vk::CreateBuffer(sizeof(UniformSceneData), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uniformSceneBuffer, + uniformSceneBufferMemory, + device); + + VkDescriptorPoolSize poolSize = {}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = 2; + + VkDescriptorPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = 1; + + vkCreateDescriptorPool(device.logicalDevice, &poolInfo, nullptr, &descriptorPool); + + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &descriptorSetLayout; + + vkAllocateDescriptorSets(device.logicalDevice, &allocInfo, &descriptorSet); + + VkDescriptorBufferInfo matrixBufferInfo = {}; + matrixBufferInfo.buffer = uniformMatrixBuffer; + matrixBufferInfo.offset = 0; + matrixBufferInfo.range = sizeof(UniformMatrixData); + + VkDescriptorBufferInfo sceneBufferInfo = {}; + sceneBufferInfo.buffer = uniformSceneBuffer; + sceneBufferInfo.offset = 0; + sceneBufferInfo.range = sizeof(UniformSceneData); + + VkWriteDescriptorSet descriptorWrites[2] = {}; + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].pNext = nullptr; + descriptorWrites[0].dstSet = descriptorSet; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &matrixBufferInfo; + + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].pNext = nullptr; + descriptorWrites[1].dstSet = descriptorSet; + descriptorWrites[1].dstBinding = 1; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].pBufferInfo = &sceneBufferInfo; + + vkUpdateDescriptorSets(device.logicalDevice, 2, descriptorWrites, 0, nullptr); + + VkBuffer vertexStagingBuffer; + VkDeviceMemory vertexStagingBufferMemory; + + //VERTICES + VkDeviceSize vertexSize = sizeof(glm::vec3) * cube.vertices.size(); + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexStagingBuffer, + vertexStagingBufferMemory, + device); + + void* data; + vkMapMemory(device.logicalDevice, vertexStagingBufferMemory, 0, vertexSize, 0, &data); + memcpy(data, cube.vertices.data(), static_cast(vertexSize)); + vkUnmapMemory(device.logicalDevice, vertexStagingBufferMemory); + + vk::CreateBuffer(vertexSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + cube.vertexBuffer, + cube.vertexBufferMemory, + device); + vk::CopyBuffer(vertexStagingBuffer, cube.vertexBuffer, vertexSize, device); + + vkFreeMemory(device.logicalDevice, vertexStagingBufferMemory, nullptr); + vkDestroyBuffer(device.logicalDevice, vertexStagingBuffer, nullptr); +} + +void SkyboxPass::Record(Camera* camera, VkCommandBuffer commandBuffer, vk::Device device) +{ + UniformMatrixData matrixData; + matrixData.proj = camera->GetProjection(); + matrixData.proj[1][1] *= -1.0f; + matrixData.view = camera->GetView(); + + void* data; + vkMapMemory(device.logicalDevice, uniformMatrixBufferMemory, 0, sizeof(matrixData), 0, &data); + memcpy(data, &matrixData, sizeof(matrixData)); + vkUnmapMemory(device.logicalDevice, uniformMatrixBufferMemory); + + UniformSceneData sceneData; + + auto lights = GetEntPool().GetOf(); + for(unsigned int i = 0; i < lights.size(); i++) { + if(lights[i]->type == Light::Type::Directional) + sceneData.sunPosition = glm::vec4(glm::normalize(lights[i]->GetEntity()->GetTransform()->GetUp()), 0.0f); + } + + data = nullptr; + vkMapMemory(device.logicalDevice, uniformSceneBufferMemory, 0, sizeof(sceneData), 0, &data); + memcpy(data, &sceneData, sizeof(sceneData)); + vkUnmapMemory(device.logicalDevice, uniformSceneBufferMemory); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, 0); + + VkBuffer vertexBuffers[] = { cube.vertexBuffer }; + VkDeviceSize offsets[] = { 0 }; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + + vkCmdDraw(commandBuffer, static_cast(cube.vertices.size()), 1, 0, 0); +} + +void SkyboxPass::Cleanup(vk::Device c) +{ + vkFreeMemory(c.logicalDevice, cube.vertexBufferMemory, nullptr); + vkDestroyBuffer(c.logicalDevice, cube.vertexBuffer, nullptr); + + vkFreeMemory(c.logicalDevice, uniformSceneBufferMemory, nullptr); + vkDestroyBuffer(c.logicalDevice, uniformSceneBuffer, nullptr); + + vkFreeMemory(c.logicalDevice, uniformMatrixBufferMemory, nullptr); + vkDestroyBuffer(c.logicalDevice, uniformMatrixBuffer, nullptr); + + vkDestroyDescriptorPool(c.logicalDevice, descriptorPool, nullptr); + + vkDestroyShaderModule(c.logicalDevice, vertexShaderModule, nullptr); + vkDestroyShaderModule(c.logicalDevice, fragmentShaderModule, nullptr); + + vkDestroyPipelineLayout(c.logicalDevice, pipelineLayout, nullptr); + vkDestroyPipeline(c.logicalDevice, pipeline, nullptr); + + vkDestroyDescriptorSetLayout(c.logicalDevice, descriptorSetLayout, nullptr); +} diff --git a/engine/runtime/CMakeLists.txt b/engine/runtime/CMakeLists.txt new file mode 100644 index 0000000..14942ef --- /dev/null +++ b/engine/runtime/CMakeLists.txt @@ -0,0 +1,34 @@ +SetMSVCOutput(${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/runtime/) + +add_executable(Runtime src/main.cpp src/assimpimporter.cpp) + +set_target_properties(Runtime PROPERTIES OUTPUT_NAME ${GAME_NAME}) + +target_link_libraries(Runtime Core Input Platform glfw) + +if(UNIX) + target_link_libraries(Runtime dl) +endif() + +add_dependencies(Runtime Core copydist) + +include_directories(${VKRENDERER_INCLUDE_DIR} ${GL45RENDERER_INCLUDE_DIR}) +include_directories(SYSTEM ${LIBRARY_GLAD_INCLUDE_DIR} ${LIBRARY_GLSLANG_INCLUDE_DIR} ${LIBRARY_SPIRV_INCLUDE_DIR}) + +include_directories( + ${CORE_INCLUDE_DIR} + ${RENDERER_INCLUDE_DIR} + ${NULLRENDERER_INCLUDE_DIR} + ${GL45RENDERER_INCLUDE_DIR} + ${UTILITY_INCLUDE_DIR} + ${PLATFORM_INCLUDE_DIR} + ${INPUT_INCLUDE_DIR} + ${ASSETS_INCLUDE_DIR}) + +include_directories(SYSTEM + ${LIBRARY_GLAD_INCLUDE_DIR} + ${LIBRARY_JSON_INCLUDE_DIR} + ${LIBRARY_VULKAN_INCLUDE_DIR} + ${LIBRARY_GLFW_INCLUDE_DIRS} + ${LIBRARY_PHYSFS_INCLUDE_DIR} + ${LIBRARY_GLM_INCLUDE_DIR}) \ No newline at end of file diff --git a/engine/runtime/src/assimpimporter.cpp b/engine/runtime/src/assimpimporter.cpp new file mode 100644 index 0000000..148920d --- /dev/null +++ b/engine/runtime/src/assimpimporter.cpp @@ -0,0 +1,9 @@ +#include "assimpimporter.hpp" + +#include + +ImportedModel AssimpImporter::Import(const std::string& path) +{ + Log::Error("Loading raw models is not supported!"); + return ImportedModel(); +} \ No newline at end of file diff --git a/engine/runtime/src/main.cpp b/engine/runtime/src/main.cpp new file mode 100644 index 0000000..5c984ad --- /dev/null +++ b/engine/runtime/src/main.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GLFWwindow* window; +Engine* engine; +GameInfo info; + +bool debugEnabled = false; + +std::vector searchPaths; + +void ParseGameConfig() +{ + std::ifstream in(std::string(GAME_FOLDER) + "/game.json"); + if(in.is_open()) + { + nlohmann::json file; + file << in; + + info.gameName = file["game_name"]; + info.defaultMap = file["default_map"]; + + nlohmann::json search_paths = file["search_paths"]; + for(auto path : search_paths) + searchPaths.push_back(path); + } + else + { + Log::Error("Failed to read game.json! Loading defaults..."); + } +} + +void ParseCommandlineArguments(int argc, char** argv) +{ + //backend overrides + for(int i = 0; i < argc; i++) + { + if(!strcmp(argv[i], "--debug")) + { + debugEnabled = true; + info.debugGUIEnabled = true; + } + + if(!strcmp(argv[i], "--benchmark")) + { + info.benchmarkingEnabled = true; + } + } +} + +void error_callback(int, const char* description) +{ + Log::Error("GLFW Error: %s", description); +} + +void resize_callback(GLFWwindow*, int width, int height) +{ + if(width == 0 || height == 0) return; + + GFX::GetScreenHeight() = height; + GFX::GetScreenWidth() = width; + + engine->GetRenderer()->Resized(); +} + +int main(int argc, char** argv) +{ + ParseCommandlineArguments(argc, argv); + ParseGameConfig(); + + if(debugEnabled) + glfwSetErrorCallback(error_callback); + + if(!glfwInit()) + { + Log::Error("Failed to initialize GLFW!"); + return -1; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + window = glfwCreateWindow(constants::defaultScreenWidth, constants::defaultScreenHeight, "Game Window", nullptr, nullptr); + if(window == nullptr) + { + Log::Error("Failed to create a GLFW window!"); + return -1; + } + + glfwSetWindowSizeCallback(window, resize_callback); + + Filesystem::IsPacked() = true; + Filesystem::Init(argv[0], searchPaths); + + Platform::SetUserData(static_cast(window)); + InputPump::Initialize(); + + //fill initial window data + GFX::GetScreenWidth() = constants::defaultScreenWidth; + GFX::GetScreenHeight() = constants::defaultScreenHeight; + + engine = new Engine(info); + + while(!glfwWindowShouldClose(window)) + { + engine->Tick(); + } + + delete engine; + glfwTerminate(); + + return 0; +} \ No newline at end of file diff --git a/engine/utility/eventdispatcher.hpp b/engine/utility/eventdispatcher.hpp new file mode 100644 index 0000000..835a4f4 --- /dev/null +++ b/engine/utility/eventdispatcher.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include +#include + +struct Listener +{ + virtual bool isValid() + { + return false; + } + + virtual void call() {} + + virtual std::type_index get_type() + { + return typeid(Listener); + } +}; + +template +struct BindedListener : public Listener +{ + std::function func; + T* caller; + + bool isValid() override + { + return caller != nullptr; + } + + void call() override + { + return func(); + } + + std::type_index get_type() override + { + return typeid(T); + } +}; + +struct FunctionListener : public Listener +{ + std::function func; + + bool isValid() override + { + return static_cast(func); + } + + void call() override + { + return func(); + } +}; + +template +class EventDispatcher; + +template +class EventDispatcher +{ +public: + void Register(std::function func) + { + auto listener = new FunctionListener; + listener->func = func; + + m_listeners.push_back(listener); + } + + template + void Unregister(T* t) + { + for(size_t i = 0; i < m_listeners.size(); i++) + { + if(std::type_index(typeid(T)) == m_listeners[i]->get_type()) + m_listeners.erase(m_listeners.begin() + i); + } + } + + template + void Register(F&& f, R* r) + { + auto listener = new BindedListener; + listener->caller = r; + listener->func = std::bind(f, r); + + m_listeners.push_back(listener); + } + + void operator()(Args&&... args) + { + //dispatch + for(size_t i = 0; i < m_listeners.size(); i++) + { + Listener* listener = m_listeners[i]; + + if(listener->isValid()) + listener->call(); + } + } + +private: + std::vector m_listeners; +}; \ No newline at end of file diff --git a/engine/utility/interdata.hpp b/engine/utility/interdata.hpp new file mode 100644 index 0000000..a9af37c --- /dev/null +++ b/engine/utility/interdata.hpp @@ -0,0 +1,283 @@ +#pragma once + +#include +#include +#include +#include + +#include "utility.hpp" + +/* + * Our own intermediate data solution (used for saving/loading, etc) + */ + +class IntermediateType +{ +public: + enum Type + { + String, + SignedInteger, + UnsignedInteger, + Float, + Vec3, + Vec2, + Quat, + Bool + }; + + struct Data + { + std::string m_string; + signed int m_integer; + unsigned int m_unsignedInteger; + float m_float; + glm::vec3 m_vec3; + glm::vec2 m_vec2; + glm::quat m_quat; + bool m_bool; + }; + + IntermediateType() {} + ~IntermediateType() {} + + IntermediateType(const IntermediateType& copy) + { + m_data = copy.m_data; + m_type = copy.m_type; + } + + IntermediateType(const std::string& value) + { + m_data.m_string = value; + m_type = String; + } + + IntermediateType(signed int value) + { + m_data.m_integer = value; + m_type = SignedInteger; + } + + IntermediateType(unsigned int value) + { + m_data.m_unsignedInteger = value; + m_type = UnsignedInteger; + } + + IntermediateType(float value) + { + m_data.m_float = value; + m_type = Float; + } + + IntermediateType(glm::vec3 value) + { + m_data.m_vec3 = value; + m_type = Vec3; + } + + IntermediateType(glm::vec2 value) + { + m_data.m_vec2 = value; + m_type = Vec2; + } + + IntermediateType(glm::quat value) + { + m_data.m_quat = value; + m_type = Quat; + } + + IntermediateType(bool value) + { + m_data.m_bool = value; + m_type = Bool; + } + + IntermediateType& operator=(const IntermediateType& rhs) + { + m_data = rhs.m_data; + m_type = rhs.m_type; + + return *this; + } + + operator std::string&() + { + return m_data.m_string; + } + + operator signed int&() + { + return m_data.m_integer; + } + + operator unsigned int&() + { + return m_data.m_unsignedInteger; + } + + operator float&() + { + //workaround: + //our json library does NOT guarantee floats will be stored as floats + if(m_type == SignedInteger) + { + m_data.m_float = static_cast(m_data.m_integer); + } + else if(m_type == UnsignedInteger) + { + m_data.m_float = static_cast(m_data.m_unsignedInteger); + } + + return m_data.m_float; + } + + operator glm::vec3&() + { + return m_data.m_vec3; + } + + operator glm::vec2&() + { + return m_data.m_vec2; + } + + operator glm::quat&() + { + return m_data.m_quat; + } + + operator bool&() + { + return m_data.m_bool; + } + + Type GetType() const + { + return m_type; + } + + Data GetData() const + { + return m_data; + } + +private: + Data m_data; + Type m_type; +}; + +class IntermediateData +{ +public: + IntermediateData() {} + ~IntermediateData() {} + + IntermediateData(const IntermediateData& inData) + { + data = inData.data; + } + + IntermediateData(nlohmann::json json) + { + for(auto it = json.begin(); it != json.end(); it++) + { + std::string key = it.key(); + + if(it.value().is_number_integer()) + { + int val = it.value(); + data[key] = val; + } + + if(it.value().is_boolean()) + { + bool val = it.value(); + data[key] = val; + } + + //crappy detection of vectors (since they are technically stored in strings) + if(it.value().is_string()) + { + std::string s = it.value(); + + size_t n = std::count(s.begin(), s.end(), ','); + switch(n) + { + case 1: + data[key] = Utility::StringToVec2(s.c_str()); //vec2 + break; + case 2: + data[key] = Utility::StringToVec3(s); //vec3 + break; + case 3: + data[key] = Utility::StringToQuat(s.c_str()); //quat + break; + default: + { + if(Utility::IsFloat(s)) + { + data[key] = stof(s); + } + else + { + data[key] = s; //if its actually a string + } + } + break; + } + } + } + } + + nlohmann::json GetJSON() + { + nlohmann::json tmp; + for(auto itr : data) + { + switch(itr.second.GetType()) + { + case IntermediateType::String: + tmp[itr.first] = itr.second.GetData().m_string; + break; + case IntermediateType::SignedInteger: + tmp[itr.first] = itr.second.GetData().m_integer; + break; + case IntermediateType::UnsignedInteger: + tmp[itr.first] = itr.second.GetData().m_unsignedInteger; + break; + case IntermediateType::Float: + tmp[itr.first] = itr.second.GetData().m_float; + break; + case IntermediateType::Vec3: + tmp[itr.first] = Utility::Vec3ToString(itr.second); + break; + case IntermediateType::Vec2: + tmp[itr.first] = Utility::Vec2ToString(itr.second); + break; + case IntermediateType::Quat: + tmp[itr.first] = Utility::QuatToString(itr.second); + break; + case IntermediateType::Bool: + tmp[itr.first] = itr.second.GetData().m_bool; + break; + } + } + + return tmp; + } + + auto& operator[](const std::string& key) + { + return data[key]; + } + + bool operator!=(IntermediateData& b) + { + return GetJSON() == b.GetJSON(); + } + + std::map data; +}; \ No newline at end of file diff --git a/engine/utility/log.hpp b/engine/utility/log.hpp new file mode 100644 index 0000000..9fc735f --- /dev/null +++ b/engine/utility/log.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +class Log +{ +public: + static void Print(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + + vprintf(std::string(fmt + std::string("\n")).c_str(), args); + + va_end(args); + } + + static void Error(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + + vfprintf(stderr, std::string(fmt + std::string("\n")).c_str(), args); + + va_end(args); + } +}; + +#ifdef VDEBUG +#define DEBUG(x, ...) Log::Print(x, __VA_ARGS__); +#else +#define DEBUG(x, ...) +#endif \ No newline at end of file diff --git a/engine/utility/module.hpp b/engine/utility/module.hpp new file mode 100644 index 0000000..59b38e7 --- /dev/null +++ b/engine/utility/module.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include + +#if defined(LINUX) || defined(EMSCRIPTEN) +#include +#define EXPORT virtual +#define LIBRARY_SUFFIX ".so" +#define HANDLE_TYPE void* +#endif + +#if WINDOWS +#define NOMINMAX +#define NOGDI +#define WIN32_LEAN_AND_MEAN +#include +#define EXPORT virtual __declspec(dllexport) +#define LIBRARY_SUFFIX ".dll" +#define HANDLE_TYPE HMODULE +#endif + +#define FACTORY(x) extern "C" x* create_object() { return new x; } + +#include + +template +class Module +{ +public: + Module(const std::string& path) + { +#ifdef LINUX + handle = dlopen((path + LIBRARY_SUFFIX).c_str(), RTLD_LAZY); + if(!handle) + { + std::cerr << dlerror() << std::endl; + return; + } + + create = reinterpret_cast(dlsym(handle, "create_object")); + const char* err = dlerror(); + if(err) + { + std::cerr << err << std::endl; + return; + } +#endif +#if WINDOWS + handle = LoadLibrary((path + LIBRARY_SUFFIX).c_str()); + if(!handle) + { + std::cerr << "Failed to load library " << path + LIBRARY_SUFFIX << std::endl; + } + create = reinterpret_cast(GetProcAddress(handle, "create_object")); +#endif + } + ~Module() + { +#ifdef LINUX + if(handle && create) + dlclose(handle); +#endif +#ifdef WINDOWS + if(handle && create) + FreeLibrary(handle); +#endif + } + + bool IsValid() + { + return handle != nullptr; + } + + T* construct() + { + return create(); + } + +private: + T* (*create)(); + HANDLE_TYPE handle = nullptr; +}; \ No newline at end of file diff --git a/engine/utility/time.hpp b/engine/utility/time.hpp new file mode 100644 index 0000000..e9e722b --- /dev/null +++ b/engine/utility/time.hpp @@ -0,0 +1,13 @@ +#pragma once + +//simple encapsulation for DT +class Time +{ +public: + static float& GetDeltaTime() + { + static float deltaTime; + + return deltaTime; + } +}; \ No newline at end of file diff --git a/engine/utility/utility.hpp b/engine/utility/utility.hpp new file mode 100644 index 0000000..e745029 --- /dev/null +++ b/engine/utility/utility.hpp @@ -0,0 +1,200 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.hpp" + +namespace Utility { + inline uint32_t Align(uint32_t val, uint32_t align) + { + return (val + align - 1) & ~(align - 1); + } + + inline void Tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = ",") + { + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) + { + tokens.push_back(str.substr(lastPos, pos - lastPos)); + + lastPos = str.find_first_not_of(delimiters, pos); + pos = str.find_first_of(delimiters, lastPos); + } + } + + inline std::string Vec3ToString(glm::vec3 vector) + { + return std::string(std::to_string(vector.x) + "," + std::to_string(vector.y) + "," + std::to_string(vector.z)); + } + + inline std::string Vec2ToString(glm::vec2 vector) + { + return std::string(std::to_string(vector.x) + "," + std::to_string(vector.y)); + } + + inline std::string QuatToString(glm::quat quaternion) + { + return std::string(std::to_string(quaternion.w) + "," + std::to_string(quaternion.x) + "," + std::to_string(quaternion.y) + "," + std::to_string(quaternion.z)); + } + + inline glm::vec3 StringToVec3(const std::string value) + { + std::vector float_list; + + Tokenize(value, float_list); + + float x = std::stof(float_list[0]); + float y = std::stof(float_list[1]); + float z = std::stof(float_list[2]); + + return glm::vec3(x, y, z); + } + + inline glm::vec2 StringToVec2(const char* value) + { + std::string vec2_string = std::string(value); + std::vector float_list; + + Tokenize(vec2_string, float_list); + + float x = std::stof(float_list[0]); + float y = std::stof(float_list[1]); + + return glm::vec2(x, y); + } + + inline glm::quat StringToQuat(const char* value) + { + std::string quat_string = std::string(value); + std::vector float_list; + + Tokenize(quat_string, float_list); + + float w = std::stof(float_list[0]); + float x = std::stof(float_list[1]); + float y = std::stof(float_list[2]); + float z = std::stof(float_list[3]); + + return glm::quat(w, x, y, z); + } + + inline std::string RemoveToLastSlash(const std::string& str) + { + std::size_t found = str.find_last_of("/\\"); + return str.substr(0, found); + } + + inline uint32_t Random(uint32_t min, uint32_t max) + { + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution dis(min, max); + + return dis(eng); + } + + inline bool IsInteger(const std::string& str) + { + return str.find_first_of("0123456789") == std::string::npos; + } + + inline bool IsFloat(const std::string& str) + { + return str.find_first_not_of("0123456789.") == std::string::npos; + } + + inline std::string GetTypeName(std::type_index type) + { + std::string n = std::string(type.name()); + + n.erase(std::remove_if(n.begin(), n.end(), &isdigit), n.end()); + + //MSVC has the prefix "class " so we need to remove that + #ifdef OS_WIN + n = n.erase(0, 6); + #endif + + return n; + } + + inline float Lerp(float a, float b, float f) + { + return a + f * (b - a); + } + + inline std::vector ReadFile(const std::string& filename) + { + std::ifstream file_stream(filename, std::ios::ate | std::ios::binary); + + if(!file_stream.is_open()) + return std::vector(); + + size_t file_size = static_cast(file_stream.tellg()); + std::vector buffer(file_size); + + file_stream.seekg(0); + file_stream.read(buffer.data(), file_size); + + file_stream.close(); + + return buffer; + } + + inline std::string GetFilename(const std::string& path) + { + return path.substr(path.find_last_of("/") + 1, path.length()); + } + + inline std::string RemoveFileExtension(const std::string& path) + { + return path.substr(0, path.find_last_of(".")); + } + + inline std::string RemoveSubstrings(const std::string& original, std::vector substrings) + { + std::string modified = original; + + for(unsigned int i = 0; i < substrings.size(); i++) + { + std::string::size_type pos = original.find(substrings[i]); + + if (pos != std::string::npos) + modified.erase(pos, substrings[i].length()); + } + + return modified; + } + + inline int Clamp(int value, int lower, int upper) + { + return value <= lower ? lower : value >= upper ? upper : value; + } + + inline float Clip(float n, float lower, float upper) + { + return std::max(lower, std::min(n, upper)); + } + + template + inline bool RemoveFromMap(std::map& map, const A& value) + { + auto itr = map.find(value); + if(itr != map.end()) + { + map.erase(itr); + return true; + } + + return false; + } +} diff --git a/paths.cmake b/paths.cmake new file mode 100644 index 0000000..2b699a2 --- /dev/null +++ b/paths.cmake @@ -0,0 +1,22 @@ +set(LIBRARY_JSON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/json/src) +set(LIBRARY_IMGUI_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/imgui/include) +set(LIBRARY_STB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/stb) +set(LIBRARY_GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/glad/include) +set(LIBRARY_GLSLANG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/glslang) +set(LIBRARY_ANGELSCRIPT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/angelscript/include) +set(LIBRARY_VULKAN_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/vulkan/include) +set(LIBRARY_GLFW_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/glfw/include) +set(LIBRARY_PHYSFS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/physfs) +set(LIBRARY_ASSIMP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/assimp/include) +set(LIBRARY_GLM_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/glm) +set(LIBRARY_BULLET_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/bullet3/src) +set(LIBRARY_TWM_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/ToolWindowManager/include) + +set(CORE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/core/include) +set(ECS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/ecs/include) +set(RENDERER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/renderer/include) +set(ASSETS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/assets/include) +set(INPUT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/input/include) +set(PLATFORM_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/platform/common) + +set(UTILITY_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/engine/utility) \ No newline at end of file